文章目录
- 头文件
#include <pthread.h>
- 使用gcc编译在链接时需要使用 -lpthread 链接线程库
- 一个进程的线程共享数据段(.text,data,bss,heap,共享库)但有独立的栈空间
1、线程创建
1.1、函数声明
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
- thread:要创建的线程ID的地址
可用pthread_self获取线程ID- attr:创建线程时的线程属性,一般为NULL
- start_routine:返回值是void *的函数指针,即线程的运行代码
- arg:函数参数的地址
- 成功:返回 0
- 失败:错误码
EAGAIN:超出系统限制,如创建的线程过多
1.2、无参和传参测试
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *my_pthread1(void *arg){
char buf[] = "线程1";
char *retval = buf;
pthread_t *id = arg;
printf("线程1:线程id:%lu\n",*id);
sleep(3);
printf("线程1:retval指向的地址:%p,retval的地址:%p,内容:%s\n",retval,&retval,retval);
}
void *my_pthread2(void *arg){
sleep(1);
char buf[] = "线程2";
char *retval = buf;
pthread_t *id = (pthread_t *) pthread_self();
printf("线程2:线程id:%lu\n",*id);
printf("线程2:retval指向的地址:%p,retval的地址:%p,内容:%s\n",retval,&retval,retval);
}
int main() {
pthread_t id1,id2;
pthread_create(&id1,NULL,my_pthread1,&id1);
pthread_create(&id2, NULL, (void *)my_pthread2, NULL);
sleep(4);
printf("主线程:结束\n");
return 0;
}
可以看到,线程所用的地址一样,但内容却不会被其他线程改变
2、线程退出
2.1、主动退出
void pthread_exit(void *retval);
- retval:指向终止信息
无返回值
- 线程调用该函数会直接结束该线程
- 线程调用exit函数会直接结束整个进程
- 主线程调该函数只会终止本身,进程和进程内的其他线程依然存在
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int count;
void *my_pthread1(void){
printf("线程1: 开始 \n");
for (int i= 0 ;i<10;i++){
sleep(1);
if(count >2){
printf("线程1: 第%d秒结束 \n",i+1);
pthread_exit(NULL);//该线程终止
//exit(0);//整个进程终止
}
}
}
void *my_pthread2(void){
int i= 0;
printf("线程2:开始\n");
for (i= 0 ;i<6;i++){
count++;
sleep(1);
}
printf("线程2:第%d秒结束\n",i+1);
}
int main() {
pthread_t id1,id2;
count = 1;
pthread_create(&id1,NULL,(void *)my_pthread1,NULL);
pthread_create(&id2, NULL, (void *)my_pthread2, NULL);
for(int i = 0;i<10;i++){
if(count > 3){
printf("主线程: 第%d秒结束 \n",i+1);
pthread_exit(NULL);//调用此函数,主线程终止
}
sleep(1);
}
printf("====\n");//不会打印
return 0;
}
2.2、被动退出
int pthread_cancel(pthread_t thread);//关闭指定线程
int pthread_setcancelstate(int state, int *oldstate);//可设置可关闭状态
int pthread_setcanceltype(int type, int *oldtype);//设置可关闭类型
void pthread_testcancel(void);//检查是否处于被关闭的状态,是就直接关闭
- thread:待关闭线程的ID
- state:设置关闭状态
PTHREAD_CANCEL_ENABLE:可关闭,这是所有新线程的默认取消状态
PTHREAD_CANCEL_DISABLE:不可关闭- type:设置关闭类型
PTHREAD_CANCEL_DEFERRED:延迟关闭(这里不是很理解?)
PTHREAD_CANCEL_ASYNCHRONOUS:立即关闭
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *my_pthread1(void *arg){
printf("线程1:开始\n");
sleep(2);
pthread_t *id = arg;
pthread_cancel(*id);
printf("线程1:结束\n");
}
void *my_pthread2(void *arg){
//pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
//pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,NULL);
for (int i = 0;i<6;i++){
printf("线程2:运行%d秒\n",i+1);
sleep(1);
//pthread_testcancel();
}
}
int main() {
pthread_t id1,id2;
pthread_create(&id1,NULL,my_pthread1,&id2);
pthread_create(&id2, NULL, (void *)my_pthread2, NULL);
sleep(6);
printf("主线程:结束\n");
return 0;
}
主要了解pthread_cancel,其他的实在有点搞不懂
3、线程等待
int pthread_join(pthread_t thread, void **retval);
- thread:要等待线程的ID
- retval:存储被等待线程的终止信息
至于为啥是二级指针,猜测pthread_exit(void *retval)函数传出的是retval这个指针的地址
- 成功: 0
- 失败:错误码;会立即返回
ESRCH:未找到指定ID的线程
EDEADLK:等待会出现死锁,如等待自身,相互等待
EINVAL:该ID的线程是分离线程
- 线程调用函数成功会阻塞自己,直到被等待线程结束
- 多个线程等待同一线程,只有最先开始等待可以等待成功,其余的将继续阻塞(猜测)
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <asm-generic/errno.h>
#include <string.h>
#include <malloc.h>
void *my_pthread1(void *arg){
pthread_t* id = (pthread_t *)arg;
printf("线程1:开始等待\n");
int ret = pthread_join(*id,NULL);
if(ret == ESRCH){
printf("线程1:id对应的线程不存在\n");
sleep(3);
char *buf = (char *)malloc(sizeof(char)*10);
strncpy(buf,"123456",7);
printf("线程1:buf指向的地址:%p,buf的地址%p,内容:%s\n",buf,&buf,buf);
printf("线程1:结束\n");
pthread_exit(buf);
} else{
printf("线程1 =结束=\n");
}
}
void *my_pthread2(void *arg){
pthread_t* id = (pthread_t *)arg;
char *retval = NULL;
printf("线程2:开始等待\n");
int ret = pthread_join(*id, (void **) &retval);
if(ret == ESRCH){
printf("线程2:id对应的线程不存在\n");
} else{
printf("线程2:retval指向的地址:%p,retval的地址:%p,内容:%s\n",retval,&retval,retval);
free(retval);
retval = NULL;
printf("线程2: =结束=\n");
}
}
void *my_pthread3(void *arg){
pthread_t* id = (pthread_t *)arg;
char *retval = NULL;
printf("线程3:开始等待\n");
int ret = pthread_join(*id, (void **) &retval);
if(ret == ESRCH){
printf("线程3:id对应的线程不存在\n");
} else{
printf("线程3:retval指向的地址:%p,retval的地址:%p,内容:%s\n",retval,&retval,retval);
free(retval);
retval = NULL;
printf("线程3: =结束=\n");
}
}
int main() {
pthread_t id1,id2,id3,id4 = 0;
pthread_create(&id1,NULL,(void *)my_pthread1,&id4);
pthread_create(&id2, NULL, (void *)my_pthread2, &id1);
pthread_create(&id3, NULL, (void *)my_pthread3, &id1);
//pthread_join(id2,NULL);
sleep(5);
printf("主线程:结束\n");
return 0;
}
4、线程属性
4.1、线程属性结构
typedef struct {
int detachstate;//线程分离状态
int schedpolicy;//线程调度策略
struct sched_param schedparam;//线程调度参数
int inheritsched;//线程的继承性
int scope;//线程的作用域
size_t guardsize;//线程栈末尾的警戒缓冲区大小
int stackaddr_set;//线程栈的设置
void * stackaddr;//线程栈位置
size_t stacksize;//线程栈大小
}pthread_attr_t;
虽然书上有,但我在 pthreadtypes.h 中没找到这个结构体,只有一个共用体
4.2、线程属性初始化
int pthread_attr_init(pthread_attr_t *attr);//初始化
int pthread_attr_destroy(pthread_attr_t *attr);//去初始化
- attr:指向线程属性的指针
- 返回值:
成功 0;
失败 错误号;
4.3、线程分离
- 一个线程要么是可结合的(joinable)要么是分离的(detached)
- 可结合的线程能够被其他线程关闭和回收资源
- 分离的线程不能被其他线程关闭(?)和回收资源,结束时由系统自动释放资源
//通过线程id设置线程分离
int pthread_detach(pthread_t thread);
//在线程内设置线程分离
int pthread_attr_setdetachstate(pthread_attr_t *attr,
int detachstate);
//获取线程分离状态
int pthread_attr_getdetachstate(const pthread_attr_t *attr,
int *detachstate);
- thread:线程ID
- attr:指向线程属性的指针
- detachstate:指向线程分离状态的指针
PTHREAD_CREATE_DETACHED:设置为分离
PTHREAD_CREATE_JOINABLE:设置为非分离(默认)
- 返回值:
成功 0;
失败 错误号;
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <malloc.h>
pthread_t id,id2;
void *fun1(void *arg){
printf("线程1:开始\n");
/*设置为分离线程 方法2*/
// pthread_attr_t attr;
// int state;
// state = PTHREAD_CREATE_JOINABLE;
// state = PTHREAD_CREATE_DETACHED;
// pthread_attr_setdetachstate(&attr,state);
for(int i= 0;i<6;i++){
printf("线程1: %d\n",i+1);
sleep(1);
}
printf("线程1:结束\n");
}
void *fun2(void *arg){
sleep(1);
printf("线程2:开始\n");
if(pthread_cancel(id) == 0){
printf("线程2:关闭线程1\n");
}
}
int main() {
pthread_create(&id,NULL,(void *)fun1,NULL);
pthread_detach(id);//设置为分离线程 方法1
sleep(1);
pthread_create(&id2,NULL,(void *)fun2,NULL);
pthread_join(id,NULL);
printf("线程1结束?\n");
sleep(10);
printf("主线程:结束\n");
return 0;
}
- 分离线程不能被等待可以确定,但可以被其他线程关闭,虽然书上说的不能被关闭
5、线程同步
5.1、互斥锁
- 若互斥锁已经被上锁,线程再想加锁就会被阻塞,直到互斥锁被加锁的线程解锁
/*初始化*/
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *mutexattr);
/*操作函数*/
int pthread_mutex_lock(pthread_mutex_t *mutex);//上锁,被锁住时阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex);//被锁住时返回,否则上锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);//解锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);//清除锁
- mutex:互斥锁
- mutexattr:互斥锁属性
- 返回值:
成功 0;
失败 错误码;
5.2、条件变量
- 使用条件变量(pthread_cond_wait)会使加锁线程阻塞并解开互斥锁
/*初始化*/
int pthread_cond_init (pthread_cond_t *cond,const pthread_condattr_t *ond_attr);
int pthread_cond_signal (pthread_cond_t *cond); //发送激活信号
int pthread_cond_wait (pthread_cond_t *cond,pthread_mutex_t *mutex);//等待激活
- cond:条件变量
- mutex:互斥锁
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
/*用于线程同步*/
struct thread_synchronization{
pthread_cond_t cond;
pthread_mutex_t mutex;
int count;
};
void *time_count(void *arg);
void *func1(void *arg);
void *func2(void *arg);
void *func3(void *arg);
int main() {
pthread_t id,id1,id2,id3;
struct thread_synchronization ts;
int ret =0;
ts.count = 0;
/*互斥量初始化*/
ret = pthread_mutex_init(&(ts.mutex), NULL);
if(ret != 0){
perror("pthread_mutex_init error");
exit(-1);
}
/*条件变量初始化*/
ret = pthread_cond_init(&(ts.cond), NULL);
if(ret != 0){
perror("pthread_cond_init error");
exit(-1);
}
/*创建记时线程 */
ret = pthread_create(&id, NULL, time_count, &ts);
if(ret != 0){
perror("pthread_create 1 error");
exit(-1);
}
/*隔1秒后创建线程1 */
sleep(1);
ret = pthread_create(&id1, NULL, func1, &ts);
if(ret != 0){
perror("pthread_create 1 error");
exit(-1);
}
/*隔1秒后创建线程2*/
sleep(1);
ret = pthread_create(&id2, NULL, func2, &ts);
if(ret != 0){
perror("pthread_create 2 error");
exit(-1);
}
/*隔1秒后创建线程3*/
sleep(1);
ret = pthread_create(&id3, NULL, func3, &ts);
if(ret != 0){
perror("pthread_create 3 error");
exit(-1);
}
pthread_join(id1,NULL);
return 0;
}
void *time_count(void *arg){
struct thread_synchronization *ts = arg;
printf("记时线程:启动\n");
while(ts->count < 20 ){
ts->count++;
sleep(1);
}
}
void *func1(void *arg){
struct thread_synchronization *ts = arg;
printf("线程1:在第%d秒启动 \n",ts->count);
while(ts->count < 10) {
pthread_mutex_lock(&(ts->mutex));
if(ts->count % 2 == 0){
printf("线程1:在第%d秒等待信号\n",ts->count);
pthread_cond_wait(&(ts->cond), &(ts->mutex));
printf("线程1:在第%d秒成功接收\n",ts->count);
printf("\n");
}
pthread_mutex_unlock(&(ts->mutex));
sleep(1);
}
}
void *func2(void *arg){
struct thread_synchronization *ts = arg;
printf("线程2:在第%d秒启动 \n",ts->count);
while(ts->count < 10) {
pthread_mutex_lock(&(ts->mutex));
if(ts->count %2 != 0){
printf("线程2:在第%d秒信号发送\n",ts->count);
pthread_cond_signal(&(ts->cond));
}
pthread_mutex_unlock(&(ts->mutex));
sleep(1);
}
}
void *func3(void *arg){
struct thread_synchronization *ts = arg;
printf("线程3:在第%d秒启动 \n",ts->count);
pthread_mutex_lock(&(ts->mutex));
printf("线程3:在第%d秒将使用互斥区3s \n",ts->count);
sleep(3);
pthread_mutex_unlock(&(ts->mutex));
}
5.3、信号量
- 头文件
#include <semaphore.h>
- 初始化
int sem_init(sem_t *sem, int pshared, unsigned int value);
- sem:信号量
- pshared:判断信号量用于进程还是线程
用于线程:0
用于进程:非0- value:指定信号量的初始值
- 返回值:
成功:0
失败:-1
- 信号量操作函数
int sem_wait(sem_t *sem);//等待信号量
/*
如果信号量的值大于0,将信号量的值减1,立即返回。
如果信号量的值为0,则线程阻塞。相当于P操作。
*/
int sem_post(sem_t *sem);//释放信号量,让信号量的值加1。相当于V操作。
- 返回值:
成功:0
失败:-1
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#include <semaphore.h>
sem_t sem1;
sem_t sem2;
char input_buf[1024];
void *fun(void *arg) {
while (1) {
sem_wait(&sem1);
printf("线程1:用户输入了%ld个字节 \n", strlen( input_buf));
sem_post(&sem2);
}
}
int main() {
pthread_t id;
if (sem_init(&sem1, 0, 0) == -1) {
perror("sem1_init");
exit(-1);
}
if (sem_init(&sem2, 0, 1) == -1) {
perror("sem2_init");
exit(-1);
}
if (pthread_create(&id, NULL, fun, NULL) != 0) {
fprintf(stderr, "pthread_create %s\n", strerror(errno));
exit(-1);
}
do {
sem_wait(&sem2);
printf("主线程:请输入:");
fflush(stdout);
fgets(input_buf, sizeof(input_buf), stdin);
input_buf[strlen(input_buf) -1] = 0;
sem_post(&sem1);
} while (strncmp(input_buf, "退出", 6) !=0);
return 0;
}
参考:man手册、《高质量嵌入式编程 》梁庚 296-306页