淺談一下linux線程
1.線程是進程中最小執行單元,多線程共享同一個進程的地址空間
2.Linux 內核調度的對象是線程,所以一個進程中多個線程參與操作操作系統統一調度
使用線程優點:
<1>效率高
<2>線程之間通信比較簡單(全局變量)
使用線程缺點:
安全性差
線程API
1.線程創建
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
參數:
@thread 獲取線程ID
@attr 設置線程屬性 NULL:默認屬性
@start_routine 線程執行的函數
@arg 給線程執行函數傳遞的參數
返回值:
成功返回0, 失敗返回錯誤碼
實例如下:
#include <head.h> void *output(void *arg) { int data = *(int *)arg; printf("%d : %d\n",pthread_self(),data); return; } int main(int argc, const char *argv[]) { int ret; int count = 0; int data = 0; pthread_t tid; while(1) {/*bug : * 一個線程被創建後,不一定立即運行,而每個線程都是從data的地址中讀取數據 *,而data地址的數據又一直在發生變化,這樣可能會導致,線程運行的時候,從data地址讀取 *的數據已經不是它想要的數據了。 * */ ret = pthread_create(&tid,NULL,output,&data); if(ret != 0){ fprintf(stderr,"Fail to pthread_create : %s\n",strerror(ret)); exit(EXIT_FAILURE); } i ++; pthread_detach(tid); data ++; count ++; printf("count = %d!\n",count); // pthread_join(tid,NULL); } return 0; }
運行如下
實例如下
創建兩個子線程,子1線程輸出data值,子2線程data ++操作,主線程將data -- 操作 (data 在主線程定義)
#include <head.h> void *output_data(void *arg) { int data = *(int *)arg; while(1) { data = *(int *)arg; printf("data = %d!\n",data); // sleep(1); } } void *add_data(void *arg) { int *pdata = (int *)arg; while(1) { (*pdata) ++; } } int main(int argc, const char *argv[]) { int ret; pthread_t tid1,tid2; int data = 10; ret = pthread_create(&tid1,NULL,output_data,&data); if(ret != 0){ fprintf(stderr,"Fail to pthread_create : %s\n",strerror(ret)); exit(EXIT_FAILURE); } ret = pthread_create(&tid2,NULL,add_data,&data); if(ret != 0){ fprintf(stderr,"Fail to pthread_create : %s\n",strerror(ret)); exit(EXIT_FAILURE); } while(1) { data --; } exit(EXIT_SUCCESS); }
運行結果
可見線程的運行並沒有先後順序
2.線程退出
(1)線程函數返回 (return)
(2)pthread_exit
(3)pthread_cancel
(4)進程結束,這個進程中所有的線程都退出
void pthread_exit(void *retval);
功能:用來退出一個線程
參數:
@retval 返回一個地址值
int pthread_join(pthread_t thread, void **retval);
功能:等待一個線程退出,將退出線程未釋放的資源釋放掉
參數:
@thread 需要等待退出線程的ID
@retval 獲得線程退出返回的值
void *thread_function(void *arg)
{
int data = 100;
pthread_exit(&data);
}
int main()
{
int *p;
pthread_join(tid,&p);
return 0;
}
3.將線程標記分離
分離狀態的線程在結束的時候,系統自動回收它的資源
int pthread_detach(pthread_t thread);
@thread 線程ID
實例如下:
(1)全局int data[] = {1,2,3,4,5,6,7,8,9,10,11};
(2)線程1一直對全局數組做逆置
(3)線程2一直對全局數組輸出
#include <head.h> //pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t lock; int a[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20}; void *output_array(void *arg) { int i = 0; //實現效果:如果發現逆置操作沒有結束,則不輸出此時阻塞自己 while(1) { pthread_mutex_lock(&lock); for(i = 0;i < 20;i ++){ printf("%d ",a[i]); } printf("\n"); pthread_mutex_unlock(&lock); } } void *reverse_array(void *arg) { int t; int i ,j; //實現效果:如果輸出操作沒有結束,則不逆置阻塞字節 while(1) { pthread_mutex_lock(&lock); i = 0; j = 19; while(i < j){ t = a[i]; a[i] = a[j]; a[j] = t; i ++; j --; } pthread_mutex_unlock(&lock); } } int main(int argc, const char *argv[]) { int ret; pthread_t tid1,tid2; int data = 10; ret = pthread_mutex_init(&lock,NULL); if(ret != 0){ fprintf(stderr,"Fail to pthread_mutex_init"); exit(EXIT_FAILURE); } ret = pthread_create(&tid1,NULL,output_array,NULL); if(ret != 0){ fprintf(stderr,"Fail to pthread_create : %s\n",strerror(ret)); exit(EXIT_FAILURE); } ret = pthread_create(&tid2,NULL,reverse_array,NULL); if(ret != 0){ fprintf(stderr,"Fail to pthread_create : %s\n",strerror(ret)); exit(EXIT_FAILURE); } pthread_join(tid1,NULL); exit(EXIT_SUCCESS); }
運行結果
並沒有出現還沒有逆置完成就輸出的
但如果去掉
pthread_mutex_lock(&lock);
pthread_mutex_unlock(&lock);
呢?
線程互斥鎖
功能:對共享資源實現互斥訪問,保證訪問的完整性
如果使用互斥鎖,每個線程在訪問共享資源,都必須先獲得互斥鎖,然後在訪問共享資源,如果無法獲得互斥鎖
,則表示有其他線程正在訪問共享資源,此時沒有獲得鎖的線程會阻塞,直到其他線程釋放鎖,能再次獲得鎖
1.定義互斥鎖[全局,讓每個線程都可以訪問到]
pthread_mutex_t lock;
2.初始化互斥鎖
//[線程創建之前]動態初始化,屬性使用默認 NULL
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
//靜態初始化定義初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
3.獲得互斥鎖
int pthread_mutex_lock(pthread_mutex_t *mutex);//操作共享資源之前加鎖
int pthread_mutex_trylock(pthread_mutex_t *mutex);
4.釋放鎖
int pthread_mutex_unlock(pthread_mutex_t *mutex);//操作共享資源結束的時候
5.銷毀鎖
int pthread_mutex_destroy(pthread_mutex_t *mutex);//不需要再次使用鎖的時候
線程間同步
同步:相互之間配合完成一件事情
互斥:保證訪問共享資源的完整性(有你沒我)
POSIX 線程中同步:使用信號量實現
信號量 : 表示一類資源,它的值表示資源的個數
對資源訪問:
p操作(申請資源) [將資源的值 - 1]
....
V操作(釋放資源) [將資源的值 + 1]
1.定義信號量
sem_t sem ;
2.初始化信號量
int sem_init(sem_t *sem, int pshared, unsigned int value);
參數:
@sem 信號量
@pshared 0:線程間使用
@value 初始化的信號量的值
返回值:
成功返回0,失敗返回-1
3.P操作
int sem_wait(sem_t *sem);
4.V操作
int sem_post(sem_t *sem);
實例如下:
創建兩個線程
write_thread : 從鍵盤上讀取用戶輸入的數據,寫到文件中
read_thread : 從文件中讀取數據,然後輸出
main_thread : 打開一個文件
#include <head.h> sem_t read_sem;//讀資源 sem_t write_sem;//寫資源 void *write_thread(void *arg) { char buf[1024]; int fd = *(int *)arg; while(1){ //申請寫資源 if(sem_wait(&write_sem) < 0){ perror("Fail to sem_wait"); pthread_exit(NULL); } fgets(buf,sizeof(buf),stdin); buf[strlen(buf) - 1] = ‘\0‘; write(fd,buf,strlen(buf)); //還原偏移量到上一次值 lseek(fd,-strlen(buf),SEEK_CUR); //釋放讀資源 if(sem_post(&read_sem) < 0){ perror("Fail to sem_wait"); pthread_exit(NULL); } if(strcmp(buf,"quit") == 0){ break; } } pthread_exit(NULL); } void *read_thread(void *arg) { int n; char buf[1024]; int fd = *(int *)arg; while(1){ //申請讀資源 if(sem_wait(&read_sem) < 0){ perror("Fail to sem_wait"); pthread_exit(NULL); } n = read(fd,buf,sizeof(buf)); buf[n] = ‘\0‘; printf("Read %d bytes : %s!\n",n,buf); //釋放寫資源 if(sem_post(&write_sem) < 0){ perror("Fail to sem_wait"); pthread_exit(NULL); } if(strcmp(buf,"quit") == 0){ break; } } pthread_exit(NULL); } //./a.out file int main(int argc, const char *argv[]) { int fd; int ret; pthread_t rtid; pthread_t wtid; if(argc < 2){ fprintf(stderr,"Usage : %s <filename>!\n",argv[0]); exit(EXIT_FAILURE); } fd = open(argv[1],O_RDWR | O_CREAT | O_TRUNC,0666); if(fd < 0){ fprintf(stderr,"Fail to open %s : %s\n",argv[1],strerror(errno)); exit(EXIT_FAILURE); } if(sem_init(&read_sem,0,0) < 0){ perror("Fail to sem_init"); exit(EXIT_FAILURE); } if(sem_init(&write_sem,0,1) < 0){ perror("Fail to sem_init"); exit(EXIT_FAILURE); } ret = pthread_create(&rtid,NULL,read_thread,&fd); if(ret != 0){ fprintf(stderr,"Fail to pthread_create : %s!\n",strerror(ret)); exit(EXIT_FAILURE); } ret = pthread_create(&wtid,NULL,write_thread,&fd); if(ret != 0){ fprintf(stderr,"Fail to pthread_create : %s!\n",strerror(ret)); exit(EXIT_FAILURE); } pthread_join(rtid,NULL); pthread_join(wtid,NULL); return 0; }
淺談一下linux線程