线程创建
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
功能:创建子线程 参数: - pthread_t *thread:线程号,传出参数,当线程创建成功后会赋予该值一个线程号 - const pthread_attr_t *attr:线程属性 - void *(*start_routine) (void *):线程函数,该参数类型为函数指针,表示要执行的子线程函数 - void *arg:为第三个参数使用
代码示例
#include<pthread.h>
#include<iostream>
using namespace std;
void *callback(void *args)
{
//子线程执行代码
//在子线程中输出自己的线程号
cout<<"child tid:"<<pthread_self()<<endl;
return NULL;
}
int main()
{
pthread_t tid;//传出参数
pthread_create(&tid,NULL,callback,NULL);
//主线程执行
int n=5;
for(int i=0;i<n;i++)
cout<<i<<endl;
//在主线程中输出子线程的线程号
cout<<"main thread:"<<pthread_self()<<"\tmain child tid"<<tid<<endl;
//主线程等待子线程执行完毕后回收子线程资源
pthread_join(tid,NULL);
return 0;
}
注:主函数就是主线程
线程终止
pthread_exit()
是一个线程终止函数,用于在线程内部终止当前线程的执行。它可以带有一个可选的返回值指针作为参数,用于向调用线程传递退出状态。函数原型:
#include <pthread.h> void pthread_exit(void *retval);
参数:
retval
:可选的返回值指针,表示线程的退出状态。默认情况下,retval
被设置为 0。说明:
pthread_exit()
函数会立即终止当前线程的执行,不会返回到调用者,并且不会执行任何清理操作。如果线程有被动分离属性,那么调用
pthread_exit()
函数将自动释放该线程占用的资源。如果线程没有被分离或销毁,而是等待其他线程调用
pthread_join()
获取退出状态,那么pthread_exit()
会保持线程处于静止状态,直到其他线程调用pthread_join()
获取退出状态或者进程终止。
示例:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
void *thread_func(void *arg) {
printf("子线程开始执行...\n");
pthread_exit(NULL); // 终止当前线程的执行
printf("子线程执行结束...\n"); // 这行代码不会被执行
return NULL;
}
int main() {
pthread_t thread_id;
int ret = pthread_create(&thread_id, NULL, thread_func, NULL);
if (ret != 0) {
perror("pthread_create");
exit(1);
}
pthread_join(thread_id, NULL); // 等待子线程执行完毕
printf("主线程结束...\n");
return 0;
}
在上面的示例中,主线程创建了一个子线程,并调用
pthread_join()
等待子线程结束。而在子线程内部,使用pthread_exit()
终止了子线程的执行。最终程序会输出"主线程结束...",而不会输出"子线程执行结束...",因为pthread_exit
终止了子线程的执行。
线程连接
#include <pthread.h> int pthread_join(pthread_t thread, void **retval);
功能:用于等待线程号为thread的线程结束,并获取其退出状态,调用该函数的线程将被阻塞,直到线程号为thread的线程执行完毕
参数:
thread
:要等待的线程标识符。
retval
:用于存储被等待线程的退出状态的指针。返回值:
若成功,返回 0。
若失败,返回错误码。
说明:
调用
pthread_join()
函数会使当前线程阻塞,直到指定的线程结束为止。如果被等待的线程已经结束,
pthread_join()
函数立即返回,可以通过retval
参数获取其退出状态。被等待的线程必须是可连接的,否则调用将会出错。可连接的线程通常是通过
pthread_create()
创建的。当一个线程结束时,它的退出状态由线程返回值决定,该返回值可以通过
retval
指针传递给pthread_exit()
或从线程函数显式返回。如果不关心被等待线程的退出状态,可以将
retval
参数设置为NULL
。每个线程只能被
join()
一次,所以多次调用pthread_join()
会导致错误。
代码示例
#include<pthread.h>
#include<stdio.h>
#include<iostream>
#include<unistd.h>
using namespace std;
void *callback(void *args)
{
cout<<"child pid:"<<pthread_self()<<endl;
static int value=10;
sleep(3);
return (void *)&value;
}
int main()
{
pthread_t pid;
pthread_create(&pid,NULL,callback,NULL);
int n=5;
for(int i=0;i<n;i++)
cout<<i<<endl;
cout<<"main pid:"<<pthread_self()<<"\t main child pid"<<pid<<endl;
int *val=0;
pthread_join(pid,(void **)&val);
cout<<*val<<endl;
//pthread_exit(NULL);
return 0;
}
线程分离
pthread_detach()
函数将一个线程标记为分离状态,使得该线程终止时自动释放所占用的资源。函数原型:
#include <pthread.h> int pthread_detach(pthread_t thread);
参数:
thread
:要分离的线程标识符。返回值:
若成功,返回 0。
若失败,返回错误码。
说明:
调用
pthread_detach()
函数将一个线程标记为分离状态,使得该线程终止时自动释放所占用的资源,无需其他线程调用pthread_join()
获取其退出状态。如果线程已经是分离状态,或者已经被销毁,则该函数什么也不做。
可连接的线程不应该使用
pthread_exit()
函数来终止线程,应该由其他线程调用pthread_join
来等待并获取其退出状态;而分离的线程通常会使用pthread_exit()
函数来终止自己的执行。一旦线程被标记为分离状态,就不能再将其恢复为可连接状态。
示例:
#include<pthread.h>
#include<iostream>
using namespace std;
void *callback(void *args)
{
//子线程执行代码
//在子线程中输出自己的线程号
cout<<"child tid:"<<pthread_self()<<endl;
return NULL;
}
int main()
{
pthread_t tid;//传出参数
pthread_create(&tid,NULL,callback,NULL);
//主线程执行
int n=5;
for(int i=0;i<n;i++)
cout<<i<<endl;
//在主线程中输出子线程的线程号
cout<<"main thread:"<<pthread_self()<<"\tmain child tid"<<tid<<endl;
//设置子线程分离,此时子线程结束时不需要主线程回收资源
pthread_detach(tid);
pthread_exit(NULL);
return 0;
}
线程同步
#include<unistd.h>
#include<pthread.h>
#include<iostream>
using namespace std;
int num=100;
//三个子线程同时使用该子线程函数,并访问全局变量
void *ticket(void *args)
{
int thread_num=*(int *)args;
while(num>0)
{
cout<<pthread_self()<<thread_num<<"号窗口卖票:"<<num<<endl;
num--;
}
return NULL;
}
int main()
{
pthread_t tid[3];
//创建三个子线程
for(int i=0;i<3;i++)
pthread_create(&tid[i],NULL,ticket,(void*)&i);
for(int i=0;i<3;i++)
pthread_join(tid[i],NULL);
pthread_exit(NULL);
return 0;
}
互斥锁
#include<unistd.h>
#include<pthread.h>
#include<iostream>
using namespace std;
int num=100;
pthread_mutex_t mutex;//创建互斥量
//三个子线程同时使用该子线程函数,并访问全局变量
void *ticket(void *args)
{
int thread_num=*(int *)args;
pthread_mutex_lock(&mutex);
while(1)
{
if(num>0)
{
cout<<pthread_self()<<"--"<<thread_num<<"号窗口卖票:"<<num<<endl;
num--;
}
else
{
pthread_mutex_unlock(&mutex);
break;
}
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main()
{
//初始化互斥锁
pthread_mutex_init(&mutex,NULL);
pthread_t tid[3];
//创建三个子线程
for(int i=0;i<3;i++)
pthread_create(&tid[i],NULL,ticket,(void*)&i);
for(int i=0;i<3;i++)
pthread_join(tid[i],NULL);
pthread_exit(NULL);
//销毁锁
pthread_mutex_destroy(&mutex);
return 0;
}
条件变量
生产者消费者模型
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
typedef struct Node
{
int num;
struct Node* next;
}Node;
Node *head=NULL;
pthread_mutex_t mutex;
void *producer(void *args)
{
while(1)
{
pthread_mutex_lock(&mutex);
Node *p=new Node;
p->num=rand()%100;
p->next=head;
head=p;
cout<<pthread_self()<<" produce: "<<p->num<<endl;
pthread_mutex_unlock(&mutex);
usleep(100);
}
return NULL;
}
void *customer(void *args)
{
while(1)
{
pthread_mutex_lock(&mutex);
Node *tmp=head;
if(head!=NULL)
{
head=head->next;
cout<<pthread_self()<<" custome: "<<tmp->num<<endl;
delete tmp;
pthread_mutex_unlock(&mutex);
usleep(100);
}else{
pthread_mutex_unlock(&mutex);
}
}
return NULL;
}
int main()
{
int pthread_num=5;
pthread_t ptids[pthread_num],ctids[pthread_num];
pthread_mutex_init(&mutex,NULL);
for(int i=0;i<pthread_num;i++)
{
pthread_create(&ptids[i],NULL,producer,NULL);
pthread_create(&ctids[i],NULL,customer,NULL);
}
for(int i=0;i<pthread_num;i++)
{
pthread_detach(ptids[i]);
pthread_detach(ctids[i]);
}
//防止某一个子线程执行完毕后直接销毁掉锁,导致其他子线程无法使用锁
while(1) sleep(10);
pthread_mutex_destroy(&mutex);
//退出主线程
pthread_exit(NULL);
return 0;
}
分析
在上述生产者消费者模型中,生产者不断生产新产品,消费者消费产品,当产品消耗完毕后,消费者将一直判断等待生产者生产,这消耗了消费者线程的资源,因此出现了条件变量的使用,当产品消耗完毕后,将消费者进行阻塞,此时等待生产者的生产信号之后解除阻塞进行消费
#include<iostream>
#include<pthread.h>
#include<unistd.h>
using namespace std;
typedef struct Node
{
int num;
struct Node* next;
}Node;
Node *head=NULL;
//互斥锁
pthread_mutex_t mutex;
//条件变量
pthread_cond_t cond;
void *producer(void *args)
{
while(1)
{
pthread_mutex_lock(&mutex);
Node *p=new Node;
p->num=rand()%100;
p->next=head;
head=p;
cout<<pthread_self()<<" produce: "<<p->num<<endl;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);//生产了一个产品之后就通知消费者进行消费
usleep(100);
}
return NULL;
}
void *customer(void *args)
{
while(1)
{
pthread_mutex_lock(&mutex);
Node *tmp=head;
if(head!=NULL)
{
head=head->next;
cout<<pthread_self()<<" custome: "<<tmp->num<<endl;
delete tmp;
pthread_mutex_unlock(&mutex);
usleep(100);
}else{
/*
当没有产品可以消费,就阻塞当前线程,等待生产者生产数据的信号
该函数可以在阻塞线程时对mutex进行解锁,
当数据生产信号到达后解除阻塞时,再对mutex进行上锁
*/
pthread_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
}
}
return NULL;
}
int main()
{
int pthread_num=5;
pthread_t ptids[pthread_num],ctids[pthread_num];
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond,NULL);
for(int i=0;i<pthread_num;i++)
{
pthread_create(&ptids[i],NULL,producer,NULL);
pthread_create(&ctids[i],NULL,customer,NULL);
}
for(int i=0;i<pthread_num;i++)
{
pthread_detach(ptids[i]);
pthread_detach(ctids[i]);
}
//防止某一个子线程执行完毕后直接销毁掉锁,导致其他子线程无法使用锁
while(1) sleep(10);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
//退出主线程
pthread_exit(NULL);
return 0;
}
信号量
信号量的初始化
#include <semaphore.h> int sem_init(sem_t *sem, int pshared, unsigned int value);
参数
sem
:指向要初始化的信号量的指针。需要提前声明一个sem_t
类型的变量,然后将其地址传递给该参数。
pshared
:描述信号量的类型。它可以取两个值:
如果
pshared
值为 0,则表示信号量是线程内部的,只能在同一进程的不同线程中共享。如果
pshared
值为非零(通常是 1),则表示信号量是由多个进程之间共享的。
value
:表示信号量的初始值。这个初始值应该是一个非负整数,用来表示可以同时执行的线程或者进程的数量。
代码示例
#include<iostream>
#include<pthread.h>
#include<unistd.h>
#include<semaphore.h>
using namespace std;
typedef struct Node
{
int num;
struct Node* next;
}Node;
Node *head=NULL;
//互斥锁
pthread_mutex_t mutex;
//设置信号量
sem_t psem,csem;
void *producer(void *args)
{
while(1)
{
//生产者接受到消费者信号后生产数据
sem_wait(&psem);
pthread_mutex_lock(&mutex);
Node *p=new Node;
p->num=rand()%100;
p->next=head;
head=p;
cout<<pthread_self()<<" produce: "<<p->num<<endl;
pthread_mutex_unlock(&mutex);
//生产者生产完毕后通知消费者消费
sem_post(&csem);
usleep(100);
}
return NULL;
}
void *customer(void *args)
{
while(1)
{
//消费者接受到生产的生产信号后开始消费数据
sem_wait(&csem);
pthread_mutex_lock(&mutex);
Node *tmp=head;
head=head->next;
cout<<pthread_self()<<" custome: "<<tmp->num<<endl;
delete tmp;
pthread_mutex_unlock(&mutex);
//消费者消耗数据之后给生产发信号生产数据
sem_post(&psem);
usleep(100);
}
return NULL;
}
int main()
{
int pthread_num=5;
pthread_t ptids[pthread_num],ctids[pthread_num];
pthread_mutex_init(&mutex,NULL);
//初始化信号量
//第二个参数0表示该信号量用于线程,第三个参数表示该信号量的初始值
sem_init(&psem,0,8);
sem_init(&csem,0,0);
for(int i=0;i<pthread_num;i++)
{
pthread_create(&ptids[i],NULL,producer,NULL);
pthread_create(&ctids[i],NULL,customer,NULL);
}
for(int i=0;i<pthread_num;i++)
{
pthread_detach(ptids[i]);
pthread_detach(ctids[i]);
}
//防止某一个子线程执行完毕后直接销毁掉锁,导致其他子线程无法使用锁
while(1) sleep(10);
sem_destroy(&psem);
sem_destroy(&csem);
pthread_mutex_destroy(&mutex);
//退出主线程
pthread_exit(NULL);
return 0;
}