前言
程序的進一步分化産生了線程,線程是更細粒度和更輕量級的程序
引入線程是為了更為精細粒度的配置設定CPU時間片,節省系統公共資源,更為充分和有效的配置有限運算能力
Tip: 線程,有時被稱為輕量級程序(Lightweight Process,LWP),是程式執行流的最小單元。一個标準的線程由線程ID,目前指令指針(PC),寄存器集合和堆棧組成。另外,線程是程序中的一個實體,是被系統獨立排程和分派的基本機關,線程自己不擁有系統資源,隻擁有一點兒在運作中必不可少的資源,但它可與同屬一個程序的其它線程共享程序所擁有的全部資源。一個線程可以建立和撤消另一個線程,同一程序中的多個線程之間可以并發執行。由于線程之間的互相制約,緻使線程在運作中呈現出間斷性。線程也有就緒、阻塞和運作三種基本狀态。就緒狀态是指線程具備運作的所有條件,邏輯上可以運作,在等待處理機;運作狀态是指線程占有處理機正在運作;阻塞狀态是指線程在等待一個事件(如某個信号量),邏輯上不可執行。每一個程式都至少有一個線程,若程式隻有一個線程,那就是程式本身。線程是程式中一個單一的順序控制流程。程序内一個相對獨立的、可排程的執行單元,是系統獨立排程和分派CPU的基本機關指運作中的程式的排程機關。在單個程式中同時運作多個線程完成不同的工作,稱為多線程
有一個很形象的比喻:
- 1.單程序單線程:一個人在一個桌子上吃菜
- 2.單程序多線程:多個人在同一個桌子上一起吃菜
- 3.多程序單線程:多個人每個人在自己的桌子上吃菜
多線程的問題是多個人同時吃一道菜的時候容易發生争搶,例如兩個人同時夾一個菜,一個人剛伸出筷子,結果伸到的時候菜已經被夾走了。此時就必須等一個人夾一口之後,再讓給另外一個人夾菜,也就是說資源共享就會發生沖突和争搶
- 對于 Windows 系統來說,【開桌子】的開銷很大,是以 Windows 鼓勵大家在一個桌子上吃菜。是以 Windows 多線程學習重點是要大量面對資源争搶與同步方面的問題。
- 對于 Linux 系統來說,【開桌子】的開銷很小,是以 Linux 鼓勵大家盡量每個人都開自己的桌子吃菜。這帶來新的問題是:坐在兩張不同的桌子上,說話不友善。是以,Linux 下的學習重點是大家要學習程序間通訊的方法
Tip: 引自 《多線程有什麼用》
這裡分享一下我在學習線程過程中的筆記和心得
概要
代碼示例
要求
編寫單程序多線程程式,用信号量實作一個線程A從标準終端輸入一個0-99的整數,另外一個線程B将此數平方後列印輸出,交替出現
代碼示例
thread.c
#include <stdio.h>
#include <semaphore.h> //sem_wait,sem_t,sem_post,sem_init 相關定義和聲明都包含在内
#include <pthread.h> //pthread_create,pthread_join 相關聲明都包含在内
#include <unistd.h> //getpid,getppid
sem_t alock,block; //定義兩個信号量類型
int num=0;
void thread_a(void) //定義線程A的操作内容
{
do
{
sem_wait(&alock); //消費A鎖
printf("please input a number(0-99):\n");
scanf("%d",&num); //擷取輸入數值
if(num < 0 || num > 99) //進行輸入校驗,如果超出範圍,則進行提醒,并且将數值置零
{
printf("the number is out of range [0-99]:%d\nwe will set back to 0 as default\n",num);
num=0;
}
sem_post(&block); //釋放B鎖
}while(1);
}
void thread_b(void) //定義線程B的操作内容
{
do
{
sem_wait(&block); //消費B鎖
printf("the sqr of %d is %d\n",num,num*num); //将數值和數值的平方進行列印
sem_post(&alock); //釋放A鎖
}while(1);
}
int main()
{
pthread_t ida=0,idb=0;
int ret=0,res=-1;
if( sem_init(&alock,0,1) || sem_init(&block,0,0)) //初始化兩個信号量
{
perror("sem_init");
return res;
}
ret=pthread_create(&ida,NULL,(void *)thread_a,NULL); //建立線程A
ret+=pthread_create(&idb,NULL,(void *)thread_b,NULL); //建立線程B
if(ret != 0) //如果建立出錯,則提醒并傳回
{
perror("pthread_create");
return res;
}
else printf("two threads have been created\nthreada: %lu\nthreadb: %lu\npid:%d\nppid:%d\n",ida,idb,getpid(),getppid());
pthread_join(ida,NULL); //等待線程A退出
pthread_join(idb,NULL); //等待線程B退出
printf("return to main\n");
res=0;
return res;
}
複制
編譯執行
emacs@ubuntu:~/c$ alias gtc
alias gtc='gcc -Wall -g -o'
emacs@ubuntu:~/c$ gtc thread.x thread.c -lpthread
emacs@ubuntu:~/c$ ./thread.x
two threads have been created
threada: 3078675312
threadb: 3070282608
pid:31391
ppid:30925
please input a number(0-99):
1
the sqr of 1 is 1
please input a number(0-99):
2
the sqr of 2 is 4
please input a number(0-99):
3
the sqr of 3 is 9
please input a number(0-99):
-1
the number is out of range [0-99]:-1
we will set back to 0 as default
the sqr of 0 is 0
please input a number(0-99):
100
the number is out of range [0-99]:100
we will set back to 0 as default
the sqr of 0 is 0
please input a number(0-99):
99
the sqr of 99 is 9801
please input a number(0-99):
^C
emacs@ubuntu:~/c$
複制
編譯執行過程中沒有報錯,從結果來看,符合預期
Note: 必須加上 -lpthread
參數,否則會因缺少庫檔案而報錯
emacs@ubuntu:~/c$ gtc thread.x thread.c
/tmp/ccAdj40G.o: In function `thread_a':
/home/emacs/c/thread.c:17: undefined reference to `sem_wait'
/home/emacs/c/thread.c:25: undefined reference to `sem_post'
/tmp/ccAdj40G.o: In function `thread_b':
/home/emacs/c/thread.c:34: undefined reference to `sem_wait'
/home/emacs/c/thread.c:36: undefined reference to `sem_post'
/tmp/ccAdj40G.o: In function `main':
/home/emacs/c/thread.c:46: undefined reference to `sem_init'
/home/emacs/c/thread.c:46: undefined reference to `sem_init'
/home/emacs/c/thread.c:52: undefined reference to `pthread_create'
/home/emacs/c/thread.c:53: undefined reference to `pthread_create'
/home/emacs/c/thread.c:60: undefined reference to `pthread_join'
/home/emacs/c/thread.c:61: undefined reference to `pthread_join'
collect2: ld returned 1 exit status
emacs@ubuntu:~/c$ gtc thread.x thread.c -lpthread
emacs@ubuntu:~/c$
複制
pthread_t
bits/pthreadtypes.h
中有關于 pthread_t 的定義
/* Thread identifiers. The structure of the attribute type is not
exposed on purpose. */
typedef unsigned long int pthread_t;
複制
可知
pthread_t
其實是
unsigned long int
的别名