1.線程概述
程序運行起來編程進程,進程由一個個線程構成。
eg:
沒有啟動的qq時一個程序,啟動后登錄qq,qq是一個進程,實際上進程什么都沒做,只是提供了需要的資源,打開聊天框可以和別人進行通信,這就是線程,和多個人聊天就是多線程。
每個進程中至少包含一個主線程。
線程是輕量級的進程(LWP:light weight process),再linux環境下的本質仍然是基礎。
進程是最小的資源分配單位,線程是最小的執行單位。
2.線程資源
線程只共享:
- 文件描述符
- 每種信號的處理方式
- 當前工作目錄
- 用戶ID和組ID,內存地址空間
獨占的資源:(棧區不共享)
- 線程ID
- 處理器現場和棧指針(內核棧)
- 獨立的棧空間(用戶空間棧)
- errno變量
- 信號屏蔽字
- 調度優先級
3.線程的優點和缺點
優點:
- 提高程序并發性
- 開銷小
- 數據通信、共享數據方便
缺點:
- 庫函數,不穩定
- 調試、編寫困難、gdb不支持
- 對信號支持不好
4.線程的創建
4.1 線程號
線程號在對應的進程里是唯一的。
進程號:pid_t 非負整數
線程號:pthread_t 無符號長整型
4.2 API
4.2.1?pthread_self()
#include <pthread.h>
pthread_t pthread_self(void);
函數功能:
- 獲取當前線程的線程號
返回值:
- 獲取的線程號
后續如果遇到編譯不通過,提示pthread相關,可以編譯的時候引入相關資源。 -pthread
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>int main(int argc, char const *argv[])
{pid_t pid=getpid();//獲取進程號pthread_t pt1=pthread_self();//獲取當前進程的主線程號printf("pid:%u,%lu\n",pid,pt1); return 0;
}
4.2.2?pthread_create()
#include <pthread.h>
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg );
函數功能:
- 創建一個線程
參數:
- thread:線程標識符地址。
- attr:線程屬性結構體地址,通常設置為 NULL。
- start_routine:線程函數的入口地址。函數指針。
- arg:傳給線程函數的參數。
返回值:
- 成功:0
- 失敗:非 0
線程函數不傳參:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>void *func(void *argv)
{printf("線程%lu正在運行\n", pthread_self());
}int main(int argc, char const *argv[])
{pid_t pid = getpid(); // 獲取進程號pthread_t pt1 = pthread_self(); // 獲取當前進程的主線程號printf("進程:%u,主線程:%lu\n", pid, pt1);pthread_t pt2;int ret = pthread_create(&pt2, NULL, func, NULL); // 創建線程if (ret != 0){perror("pthread_create");return 0;}// 在此處阻塞,避免線程未執行,進程就結束,進程結束,系統會回收進程資源// 線程之間是并發的,而不是簡單的從上往下執行sleep(1);return 0;
}
線程函數傳參:數組
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>void *func(void *argv)
{printf("線程:%lu 內容:%s\n", pthread_self(), (char *)argv);// 此處char*不能省,因為cpu訪問內存讀取數據,必須要約束類型
}int main(int argc, char const *argv[])
{pid_t pid = getpid(); // 獲取進程號pthread_t pt1 = pthread_self(); // 獲取當前進程的主線程號printf("進程:%u,主線程:%lu\n", pid, pt1);char buff[10] = "hello";pthread_t pt2;int ret = pthread_create(&pt2, NULL, func, buff); // 創建線程// int ret =pthread_create(&pt1,NULL,func,(void *)buff);// 以上兩種都可以,因為地址本身沒有類型,char * void*都可以,只是地址而已if (ret != 0){perror("pthread_create");return 0;}// 在此處阻塞,避免線程未執行,進程就結束,進程結束,系統會回收進程資源// 線程之間是并發的,而不是簡單的從上往下執行sleep(1);return 0;
}
線程函數傳參:整數
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>void *func(void *argv)
{*(int *)argv = 20;
}int main(int argc, char const *argv[])
{pthread_t pt1 = pthread_self();int num = 10;int ret = pthread_create(&pt1, NULL, func, &num);// int ret =pthread_create(&pt1,NULL,func,(void *)num);// 以上兩種都可以,因為地址本身沒有類型,int * void*都可以,只是地址而已if (ret != 0){perror("pthread_create");return 0;}sleep(1);printf("num=%d\n", num);return 0;
}
4.2.3 多線程創建
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
pthread_t pt1, pt2;
void *func1(void *argv)
{int count = 1;while (1){printf("線程:%lu:%s,執行了%ds\n", pt1, (char *)argv, count);count++;if (count == 5){break;}}
}
void *func2(void *argv)
{int count = 0;while (1){printf("線程:%lu:%s,執行了%ds\n", pt2, (char *)argv, count);count++;if (count == 5){break;}}
}int main(int argc, char const *argv[])
{pt1 = pthread_self();pt2 = pthread_self();int ret1 = pthread_create(&pt1, NULL, func1, NULL);if (ret1 != 0){perror("pthread_create");return 0;}int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_create");return 0;}sleep(1);return 0;
}
4.3.4 pthread_join()
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
函數功能:
- 等待線程結束(此函數會阻塞),并回收線程資源,類似進程的 wait() 函數。如果線程已經結束,那么該函數會立即返回。
參數:
- thread:被等待的線程號。
- retval:用來存儲線程退出狀態的指針的地址,二級指針
返回值:
- 成功:0
- 失敗:非 0
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
pthread_t pt1, pt2;
void *func1(void *argv)
{int count = 1;while (1){printf("線程:%lu:%s,執行了%ds\n", pt1, (char *)argv, count);count++;if (count == 5){break;}}return "func1";
}
void *func2(void *argv)
{int count = 0;while (1){printf("線程:%lu:%s,執行了%ds\n", pt2, (char *)argv, count);count++;if (count == 5){break;}}
}int main(int argc, char const *argv[])
{pt1 = pthread_self();pt2 = pthread_self();int ret1 = pthread_create(&pt1, NULL, func1, NULL);if (ret1 != 0){perror("pthread_create");return 0;}int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_create");return 0;}void *returnvalue;printf("returnvalue:%p\n",returnvalue);pthread_join(pt1,&returnvalue);//pt1 返回地址是 "func1" 這個字符串的首地址printf("pt1退出狀態:%s\n",(char *)returnvalue);return 0;
}
棧區數據不會共享,返回棧區數據導致段錯誤
函數返回棧區數據段錯誤示例
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
pthread_t pt1, pt2;void *func2(void *argv)
{int num=10;int count = 0;while (1){printf("線程:%lu:%s,執行了%ds\n", pt2, (char *)argv, count);count++;if (count == 5){break;}}return (void *)#
}int main(int argc, char const *argv[])
{pt2 = pthread_self();int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_create");return 0;}void *returnvalue;printf("returnvalue:%p\n",returnvalue);pthread_join(pt2,&returnvalue);//pt2 返回地址是 "func2" 這個字符串的首地址printf("pt2退出狀態:%s\n",(char *)returnvalue);return 0;
}
返回堆區地址:
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>void *func1(void *argv);
void *func2(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1, pt2;int ret1 = pthread_create(&pt1, NULL, func1, NULL);if (ret1 != 0){perror("pthread_creat()");}int ret2 = pthread_create(&pt2, NULL, func2, NULL);if (ret2 != 0){perror("pthread_creat()");}void *returnvalue;void *returnvalue2;pthread_join(pt1, &returnvalue); // pt1 返回地址是 "pthread_func1" 這個字符串的首地址pthread_join(pt2, &returnvalue2); // printf("pt1退出狀態取值:%s\n", (char *)returnvalue); //強轉,取字符串 結果 "pthread_func1"printf("pt2退出的地址取值:%d\n",*(int *)returnvalue2);return 0;
}void *func1(void *argv)
{int count = 1;while (1){printf("當前所在進程%u,當前線程%lu,執行了%dS\n", getpid(), pthread_self(), count);sleep(1);count++;if (count >= 6){break;}}return "pthread_func1";
}void *func2(void *argv)
{int *p = (int *)malloc(sizeof(int));*p = 10;int count = 1;while (1){printf("當前所在進程%u,當前線程%lu,執行了%dS\n", getpid(), pthread_self(), count);sleep(1);count++;if (count >= 6){break;}} return p; //不能是棧區地址,因為棧區地址不共享,每一個線程都有自己的棧
}
4.3.5pthread_detach()
一般情況下,線程終止后,其終止狀態一直保留到其它線程調用 pthread_join 獲取它的狀態為止。但是線程也可以被置為 detach 狀態,這樣的線程一旦終止就立刻回收(系統回收)它占用的所有資源,而不保留終止狀態。
所以如果回收線程資源: pthread_join與pthread_detach只能二選一
?#include <pthread.h>
int pthread_detach(pthread_t thread);
函數功能:
- 將指定線程標記為分離狀態。
參數:
- pthread :指定的要分離的線程
4.3.6?pthread_exit()
#include <pthread.h>
void pthread_exit(void *retval);
函數功能:
- 退出調用線程。一個進程中的多個線程是共享該進程的數據段,因此,通常線程退出后所占用的資源并不會釋放。
參數:
- retval:存儲線程退出狀態的指針。
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#define sucess 0
#define error 1void *pthread_func1(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1;char buff[] = "hello my thread";int ret = pthread_create(&pt1, NULL, pthread_func1, buff); // 創建線程,線程函數傳參printf("pt1=%lu\n", pt1);if (ret != 0){perror("pthread_create())");return 0;}printf("在進程%u中,主線程號:%lu\n", getpid(), pthread_self());void *return_value;pthread_join(pt1, &return_value);if (return_value == NULL) // 0{printf("success\n");}else{printf("error\n"); // 1}// free(return_value);return 0;
}void *pthread_func1(void *argv)
{for (int i = 1; i <= 5; i++){printf("線程%lu,執行了%dS\n", pthread_self(), i);sleep(1);if (i == 5){pthread_exit((void *)sucess); // void * + 數值,直接轉換為地址}}return NULL;
}
4.3.7?pthread_cancel()
#include <pthread.h>
int pthread_cancel(pthread_t thread);
函數功能:
- 殺死(取消)線程
參數:
- thread : 目標線程 ID。
返回值:
- 成功:0
- 失敗:出錯編號
線程的取消并不是實時的,而有一定的延時。
需要等待線程到達某個取消點。?
取消點:執行命令 man 7 pthreads 可以查看具備這些取消點的系統調用列表。 可粗略認為一個系統調用(進入內核)即為一個取消點,例如sleep();
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>void *myfunc1(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1;int ret = pthread_create(&pt1,NULL,myfunc1,NULL);pthread_cancel(pt1);while (1){/* code */}return 0;
}void *myfunc1(void *argv)
{while (1){printf("當前所在進程%u,當前線程%lu\n",getpid(),pthread_self());sleep(1);printf("check\n"); //cancel完不會執行,因為上面有取消點}}
5.線程的屬性
typedef struct
{
int etachstate; //線程的分離狀態
int schedpolicy; //線程調度策略
struct sched_param schedparam; //線程的調度參數
int inheritsched; //線程的繼承性
int scope; //線程的作用域
size_t guardsize; //線程棧末尾的警戒緩沖區大小
int stackaddr_set; //線程的棧設置
void* stackaddr; //線程棧的位置
size_t stacksize; //線程棧的大小
} pthread_attr_t;
提前設置分離屬性
- 先進行屬性初始化
- 設置某一個屬性
- 創建線程
- 銷毀線程屬性
5.1?pthread_attr_init()
int pthread_attr_init(pthread_attr_t *attr);
函數功能:
- 對線程屬性結構體 attr初始化
參數:
- attr 要初始化的結構體
5.2?pthread_attr_setdetachstate()
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
函數功能:
- 線程分離屬性設置
參數:
- attr:已初始化的線程屬性
- detachstate:分離狀態
PTHREAD_CREATE_DETACHED(分離線程)
PTHREAD_CREATE_JOINABLE(非分離線程)
5.3?
int pthread_attr_destroy(pthreadattrt *attr);
函數功能:
- 線程銷毀屬性設置
參數:
- attr:要銷毀的線程屬性
函數返回值:
- 成功:0;
- 失敗:錯誤號
#define _POSIX_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>void *myfunc1(void *argv);int main(int argc, char const *argv[])
{pthread_t pt1;pthread_attr_t attr;// 初始化pthread_attr_init(&attr);// 設置分離屬性pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);// 創建線程int ret = pthread_create(&pt1, &attr, myfunc1, NULL);// destorypthread_attr_destroy(&attr);while (1){/* code */}return 0;
}void *myfunc1(void *argv)
{while (1){printf("當前所在進程%u,當前線程%lu\n", getpid(), pthread_self());sleep(1);}
}