程序代碼test.c共兩個線程,一個主線程,一個讀緩存區的線程:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char globe_buffer[100];
void *read_buffer_thread(void *arg); //這里先聲明一下讀緩存的線程,具體實現寫在後面了
int main()
{
int res,i;
pthread_t read_thread;
for(i=0;i<20;i++)
globe_buffer[i]=i;
printf("\nTest thread : write buffer finish\n");
sleep(3);\\這里的3秒是多餘,可以不要。
res = pthread_create(&read_thread, NULL, read_buffer_thread, NULL);
if (res != 0)
{
printf("Read Thread creat Error!");
exit(0);
}
sleep(1);
printf("waiting for read thread to finish...\n");
res = pthread_join(read_thread, NULL);
if (res != 0)
{
printf("read thread join failed!\n");
exit(0);
}
printf("read thread test OK, have fun!! exit ByeBye\n");
return 0;
}
void *read_buffer_thread(void *arg)
{
int i,x;
printf("Read buffer thread read data : \n");
for(i=0;i<20;i++)
{
x=globe_buffer[i];
printf("%d ",x);
globe_buffer[i]=0;//清空
}
printf("\nread over\n");
}
---------------------------------------------------------------------------------
以上程序編譯:
gcc -D_REENTRANT test.c -o test.o –lpthread
運行這個程序:
$ ./test.o:
2. Linux多線程程序中有哪些變數類型,被映射到哪個地址空間,有幾個運行實例
在 Linux 多線程編程中,通常會使用以下幾種變數類型:
全局變數:定義在所有函數之外的變數,作用域在整個程序中都可見。全局變數被映射到進程的數據段中,所有線程都可以訪問它們。在多線程程序中,需要注意全局變數的並發訪問問題,避免出現競爭條件。
局部變數:定義在函數內部的變數,作用域僅限於函數內部。每個線程都有自己的棧空間,虧祥局部變數被分配在棧上,每個線程都有自己獨立的棧空間,互不幹擾。
線程私有變數:每個線程都有自己的私有變數。可以使用 pthread_key_create() 函數創建一個線程私有變數,使用 pthread_getspecific() 和 pthread_setspecific() 函數來設置和獲取線程私有變數的值。線程私有變數被映射到進程的線程局部存儲段(Thread Local Storage, TLS)中,每個線程都有自己獨立的 TLS,互不幹擾。
共享變數:被多個線程共享的變數。在多線程程序中,需要使用鎖(如互斥鎖、讀寫鎖)等機制來保護共享變數,避免出現競爭條件橡鉛。共享變數被映射到進程的數據段中,所有線程都可以訪問它們。
需要注意的是,在多線程程序中,這些變數類型在地址空間中的位置和數量都是相對復雜的,因為每個線程都有自己獨梁空好立的棧空間和 TLS,這些變數的地址在不同的線程中可能是不同的。因此,在多線程程序中,需要使用適當的同步機制來保護這些變數,以確保程序的正確性和可靠性。
3. linux多線程服務端編程 看什麼書
這本書主要分享了作者在實現公司內部的分布式服務系統中積累的多線程和網路編程方面的經驗,並介紹了C++ 在編寫這種分布式系統的服務端程序時的功能取捨與注意事項,書中的很多決策(design decision)是在這一應用場景下做出的。
這本書沒有細談分布式系統的設計,只在第9章列舉了分布式系統的挑戰及其對程序設計(服務端編程)的影響,例如可靠性、可維護性等。
4. linux用戶空間 - 多進程編程(三)
管道用於有學園關系的進程之間。
管道的pipe 系統調用實際上就是創建出來兩個文件描述符。
當父進P1程創建出 fd[2] 時,子進程P2 會繼承父進程的所有,所以也會得到pipe 的 2個 文件描述符。
所以毫無瓜葛的兩個進程,一定不會訪問到彼此的pipe。無法用管道進行通信。
管道一般是單工的。f[0]讀,f[1]寫
管道也可以適用於 兄弟進程(只要有血緣即可)。由於管道是單工的,當兩個進程之間需要雙向通信,則需要兩跟管道。
執行
ctrl-c(2號信號) + SIGUSR1 信號 綁了一個新函數。則 ctrl-c 無效。
查看進程的信號
號信號被捕獲。
將2號信號忽略掉
9號信號 kill 和19號信號 stop 不能亂搞,只能用預設。
其它信號甚至段信號也都可以捕獲。
改變程序的執行現場,修改PC指針,有些像goto,只不過返回非0值
運行結果
making segment fault
after segment fault
程序不會死。
如果不忽略 page fault
則會產生 core mp.
不停的給data 賦值,同時每隔1s會有信號進來,列印 data的值。
理論上列印出來的結果同時為0,同時為1
但並非如此,是 0,1,交替隨機的。
signal 非同步的,隨時都可以進來,所以列印出來的結果,並不是我想要的。
信號對於應用程序來說,很像中斷對於內核,都是訪問臨界區數據
信號被屏蔽,延後執行。
寫多線程的程序時,不要以為只有線程之間有競爭,其實信號也會有競爭
system v 的IPC 年代有些久遠。
有血緣關系的進程 key_t 都是相同的。
Key 是私有key IPV PRIVATE
可能用消息隊列,可能用共享內存,可能用信號量進行通訊。
利用 _pathname 路徑,約定好一條路徑。和tcp/ip地址很像,來生成一個key_t key, 用msg_get shm_get 得到共享內存or 信號量。
int id 可以理解為文件描述符 fd。
其中Sys V 的共享內存 最為常用。
一定要檢查路徑,如果僅僅有2個進程,你沒有創建路徑,兩者都是 -1(相當於大家約定好了),那當然能通信拉。但更多的進程出現,則會有問題。
一定要檢查返回值
依然依靠key,但是api 實在是太挫了。P&V 操作都是 semop. (posix 的 ipc跟為簡潔)
POSIX 共享內存當然也需要一個名字,但並不是路徑。
無論讀進程還是寫進程,都需要傳入相同的名字。
如果是unbuntu 會在以下路徑生成文件
其實 2和3 是1 的符號鏈接。 只要保證是一個就能互相通信
關鍵點,mmap 內存的屬性修改為 private 後,產生寫時,虛擬地址一樣,但是物理地址已經不同了
當然 如果子進程修改了程序背景,執行了 exec,那麼完全不一樣了,直接修改了內存邏輯。
5. 用C語言在windows或者Linux上面,編寫一個多線程程序
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
DWORD WINAPI ThreadProc(LPVOID lpParam)
{
int *pt=(int*)lpParam;
printf("I am tread %d\r\n",*pt);
}
int main()
{
const int Count=4;
int datas[Count];
DWORD dwThreadId[Count];
HANDLE hThread[Count];
int i;
for(i=0;i<Count;i++)
{
datas[i]=i+1;
hThread[i]=CreateThread(NULL,0,ThreadProc,&datas[i],0,&dwThreadId[i]);
}
WaitForMultipleObjects(Count,hThread,TRUE,INFINITE);
for(i=0;i<Count;i++)
{
CloseHandle(hThread[i]);
}
system("PAUSE");
return EXIT_SUCCESS;
}
6. 在Linux環境下,對一個設備文件進行多線程讀寫(兩個線程就行),求大神給一個簡單的程序。
配置文件為 conf.txt
測試代碼如下,注意鏈接的時候加上 -lpthread 這個參數
#include <stdio.h>
#include <errno.h> //perror()
#include <pthread.h>
#include <unistd.h> //sleep()
#include <time.h> // time()
#include <stdlib.h> //rand()
#define FD "conf.txt"
typedef void *(*fun)(void *);
struct my_struct
{
unsigned time_to_wait;
int n;
};
void *test_thread(struct my_struct *);
int main (int argc, char const *argv[])
{
FILE *fp = fopen(FD, "r");
if (fp == NULL)
{
perror(FD);
return -1;
}
srand((unsigned)time(NULL)); //初始化隨機種子
int thread_count;
fscanf(fp, "%d", &thread_count);
fclose(fp);
if (thread_count <= 0)
{
printf("線程數<1,退出程序。\n");
return -1;
}
pthread_t *ptid = (pthread_t *)malloc(sizeof(pthread_t) * thread_count); //保存線程ID
int i;
for (i = 0; i < thread_count; i++)
{
int tw = rand() % thread_count + 1; //隨機等待時間
struct my_struct * p = (struct my_struct *)malloc(sizeof(struct my_struct));
if (p == NULL)
{
perror("內存分配錯誤");
goto ERROR;
}
p->time_to_wait = tw;
p->n = i + 1;
int rval = pthread_create(ptid + i, NULL, (fun) test_thread, (void *)(p)); //注意這里的強制轉換(兩個)
if (rval != 0)
{
perror("Thread creation failed");
goto ERROR;
}
//sleep(1); //這句加也可以,不加也可以。最開始的時候加上這個是為了讓兩個線程啟動的時候之間有一定的時間差
}
printf("主線程啟動\n\n");
fflush(stdout);
for (i = 0; i < thread_count; i++)
{
pthread_join(*(ptid + i), NULL); //等待所有線程退出。
}
printf("\n主線程退出\n");
ERROR:
free(ptid);
return 0;
}
void *test_thread(struct my_struct * p) //線程啟動的時候運行的函數
{
printf("第%d個線程啟動,預計運行%d秒\n", p->n, p->time_to_wait);
fflush(stdout);
sleep(p->time_to_wait); //讓線程等待一段時間
printf("第%d個線程結束\n", p->n);
fflush(stdout);
free(p);
return NULL;
}
你的第二個問題我在網路HI回你了~
7. 關於linux下多線程編程
pthread_join 線程停止等待函數沒有調用
pthread_create 線程生成後,沒有等子線程停止,主線程就先停止了。
主線程停止後,整個程序停止,子線程在沒有printf的時候就被結束了。
結論:不是你沒有看到結果,而是在子線程printf("..................\n");之前整個程序就已經停止了。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#define FALSE -1
#define TRUE 0
void *shuchu( void *my )
{
int j;
printf("..................\n");
}
int main()
{
int i = 0;
int rc = 0;
int ret1;
pthread_t p_thread1;
if(0!=(ret1 = pthread_create(&p_thread1, NULL, shuchu, NULL)))printf("sfdfsdfi\n");
printf("[%d]\n",p_thread1);
pthread_join(p_thread1, NULL);
return TRUE;
}
8. Linux 多線程編程(二)2019-08-10
三種專門用於線程同步的機制:POSIX信號量,互斥量和條件變數.
在Linux上信號量API有兩組,一組是System V IPC信號量,即PV操作,另外就是POSIX信號量,POSIX信號量的名字都是以sem_開頭.
phshared參數指定信號量的類型,若其值為0,就表示這個信號量是當前進程的局部信號量,否則該信號量可以在多個進程之間共享.value值指定信號量的初始值,一般與下面的sem_wait函數相對應.
其中比較重要的函數sem_wait函數會以原子操作的方式將信號量的值減一,如果信號量的值為零,則sem_wait將會阻塞,信號量的值可以在sem_init函數中的value初始化;sem_trywait函數是sem_wait的非阻塞版本;sem_post函數將以原子的操作對信號量加一,當信號量的值大於0時,其他正在調用sem_wait等待信號量的線程將被喚醒.
這些函數成功時返回0,失敗則返回-1並設置errno.
生產者消費者模型:
生產者對應一個信號量:sem_t procer;
消費者對應一個信號量:sem_t customer;
sem_init(&procer,2)----生產者擁有資源,可以工作;
sem_init(&customer,0)----消費者沒有資源,阻塞;
在訪問公共資源前對互斥量設置(加鎖),確保同一時間只有一個線程訪問數據,在訪問完成後再釋放(解鎖)互斥量.
互斥鎖的運行方式:串列訪問共享資源;
信號量的運行方式:並行訪問共享資源;
互斥量用pthread_mutex_t數據類型表示,在使用互斥量之前,必須使用pthread_mutex_init函數對它進行初始化,注意,使用完畢後需調用pthread_mutex_destroy.
pthread_mutex_init用於初始化互斥鎖,mutexattr用於指定互斥鎖的屬性,若為NULL,則表示默認屬性。除了用這個函數初始化互斥所外,還可以用如下方式初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER。
pthread_mutex_destroy用於銷毀互斥鎖,以釋放佔用的內核資源,銷毀一個已經加鎖的互斥鎖將導致不可預期的後果。
pthread_mutex_lock以原子操作給一個互斥鎖加鎖。如果目標互斥鎖已經被加鎖,則pthread_mutex_lock則被阻塞,直到該互斥鎖佔有者把它給解鎖.
pthread_mutex_trylock和pthread_mutex_lock類似,不過它始終立即返回,而不論被操作的互斥鎖是否加鎖,是pthread_mutex_lock的非阻塞版本.當目標互斥鎖未被加鎖時,pthread_mutex_trylock進行加鎖操作;否則將返回EBUSY錯誤碼。注意:這里討論的pthread_mutex_lock和pthread_mutex_trylock是針對普通鎖而言的,對於其他類型的鎖,這兩個加鎖函數會有不同的行為.
pthread_mutex_unlock以原子操作方式給一個互斥鎖進行解鎖操作。如果此時有其他線程正在等待這個互斥鎖,則這些線程中的一個將獲得它.
三個列印機輪流列印:
輸出結果:
如果說互斥鎖是用於同步線程對共享數據的訪問的話,那麼條件變數就是用於在線程之間同步共享數據的值.條件變數提供了一種線程之間通信的機制:當某個共享數據達到某個值時,喚醒等待這個共享數據的線程.
條件變數會在條件不滿足的情況下阻塞線程.且條件變數和互斥量一起使用,允許線程以無競爭的方式等待特定的條件發生.
其中pthread_cond_broadcast函數以廣播的形式喚醒所有等待目標條件變數的線程,pthread_cond_signal函數用於喚醒一個等待目標條件變數線程.但有時候我們可能需要喚醒一個固定的線程,可以通過間接的方法實現:定義一個能夠唯一標識目標線程的全局變數,在喚醒等待條件變數的線程前先設置該變數為目標線程,然後採用廣播的方式喚醒所有等待的線程,這些線程被喚醒之後都檢查該變數以判斷是否是自己.
採用條件變數+互斥鎖實現生產者消費者模型:
運行結果:
阻塞隊列+生產者消費者
運行結果: