一、背景
之前學習過進程的概念,而計算機是不可能只單單運行一個進程的。我們使用fork與exec等系統調用來使進程併發執行。然而對於一個進程而言,該進程一定是有多個函數的,那麼這些函數是否可以併發執行呢?當然是可以的,這一機制就叫做線程機制。
二、一個單線程程序
#include
#include
#define NUM 5
int main()
{
void print_msg(char *);
print_msg("Hello");
print_msg("World\n");
}
void print_msg(char *m)
{
int i;
for(i=0;i printf("%s",m); fflush(stdout); sleep(1); } } 這是一個十分簡單的單線程程序,執行結果:
可以看到程序先執行完五個hello的輸出,接著執行五個world的輸出。
現在我們考慮的是如何輸出形如hello world\n hello world\n ... 這樣的結果。
這就需要函數併發的執行。
三、簡單的多線程程序
這裡使用了兩個系統調用:pthread_create , pthread_join。
一個用來創建新的線程,後一個用來等待某線程的終止。具體使用可使用man命令查看。
該源程序如下:
#include
#include
#include
#define NUM 5
int main()
{
pthread_t t1,t2;
void *thread(void *);
pthread_create(&t1,NULL,thread,(void *)"Hello");
pthread_create(&t2,NULL,thread,(void *)"World\n");
pthread_join(t1,NULL);
pthread_join(t2,NULL);
}
void *thread(void *m)
{
int i;
for(i=0;i printf("%s",m); sleep(1); } return NULL; } 該程序執行結果如下:
或許大家會有疑問,為什麼前半部分輸出的helloworld很正常,到後半部分就亂了呢。
原因是thread函數是併發執行的,也可理解為函數在同時運行,所以輸出hello與輸出world的先後順序並不是一定的,
每一次的輸出結果都有可能相同也有可能不相同。
四、共享全局變量
對比進程來學習線程,進程之間有進程之間的通信方式,進程可通過管道、socket、信號、退出/等待來進行進程間的通信。
而線程之間通過什麼通信方式來進行它們之間的分工合作呢?
我們明白,各個線程雖不相同,但都是同在同一個進程中運行的,也就是說,它們之間是共享全局變量的。通過全局變量的使用就可以完成線程之間的通信。
線程之間可通過全局變量進行合作。這裡舉一個例子:
#include
#include
#include
#define NUM 5
int counter = 0;
int main()
{
pthread_t t1;
void *print_count(void *);
int i;
pthread_create(&t1,NULL,print_count,NULL);
for(i=0;i counter++; sleep(1); } pthread_join(t1,NULL); } void *print_count(void *m) { int i; for(i=0;i printf("count = %d\n",counter); sleep(1); } return NULL; } 該進程有兩個線程併發執行,使用同一個全局變量counter,兩個線程都有對counter的訪問權,都可以對其進行訪問。 初始線程每一秒對counter進行加1操作,而新線程每一秒對counter進行一次打印操作。 這樣,我們看一下輸出結果: 我們可以看到,兩次輸出有不一樣的結果,原因就在於在新線程對全局變量counter進行打印時,可能初始線程剛剛對counter進行了加1操作, 也可能是剛打印完初始線程才對counter進行了加1操作。這就造成了輸出結果的不確定。
由此我們可以看到,使用全局變量既可進行通信,也同時是一個危險的行為。
五、總結
本文簡單介紹了併發函數的使用,其中涉及了幾個簡單的系統調用。需要注意的是,併發函數使用全局變量既方便,但也危險。本文還未涉及如何安全的對全局變量進行訪問。
閱讀更多 有理想的代碼dog 的文章