一、多线程的创建于退出
1. pthread_create(线程的创建)
pthread_create 是 POSIX 线程库中的函数,用于创建一个新的线程。
函数原型如下:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
参数说明:
- thread:指向 pthread_t 类型的指针,用于存储新创建线程的标识符。
- attr:指向 pthread_attr_t 类型的指针,用于指定线程的属性。可以传递 NULL,使用默认属性。
- start_routine:指向线程函数的指针,该函数用于执行线程的主要逻辑。
//回调函数定义
void* thread_function(void* arg)
{
// 线程逻辑代码
// 可以通过 arg 参数传递数据给线程函数
// 线程结束时可以返回一个值(通过返回类型为 void* 的指针)
return NULL;
}
- arg:传递给线程函数的参数,可以是任意类型的指针。
pthread_create 函数会创建一个新的线程,并在新线程中执行指定的线程函数 start_routine。线程函数的参数可以通过 arg 传递。
返回值:
- 0:这表示线程创建成功,并且新线程已经开始执行。
- 非0:表示线程创建失败
2.ptherad_join(等待指定线程的终止)
pthread_join 是 POSIX 线程库中的函数,用于等待指定的线程终止,并获取线程的退出状态。
函数原型如下:
int pthread_join(pthread_t thread, void **retval);
参数说明:
- thread:要等待的线程的标识符。
- retval:指向指针的指针,用于存储线程的退出状态。如果不需要获取退出状态,可以传递 NULL。
pthread_join 函数会阻塞当前线程,直到指定的线程终止。一旦线程终止,pthread_join 函数会返回,并将线程的退出状态存储在 retval 指向的位置。如果不需要获取退出状态,可以将 retval 设置为 NULL。
3.pthread_exit(线程的退出)
pthread_exit 是 POSIX 线程库中的函数,用于终止当前线程并返回一个退出状态。
函数原型如下:
void pthread_exit(void *retval);
参数说明:
- retval:线程的退出状态,可以是任意类型的指针。
pthread_exit 函数会立即终止当前线程,并将 retval 参数作为线程的退出状态。线程的退出状态可以是任意类型的指针,因为 pthread_exit 函数的参数类型是 void*。
4.pthread_cancel(线程的取消)
pthread_cancel 是 POSIX 线程库中的函数,用于取消指定的线程。
函数原型如下:
int pthread_cancel(pthread_t thread);
参数说明:
- thread:要取消的线程的标识符。
5.pthread_setcancelstate(设置线程的取消状态)
pthread_setcancelstate 是 POSIX 线程库中的函数,用于设置线程的取消状态。
函数原型如下:
int pthread_setcancelstate(int state, int *oldstate);
参数说明:
- state:要设置的取消状态,可以是以下两个值之一:
- PTHREAD_CANCEL_ENABLE:启用线程的取消功能。
- PTHREAD_CANCEL_DISABLE:禁用线程的取消功能。
- oldstate:用于存储之前的取消状态的指针
pthread_setcancelstate 函数用于设置线程的取消状态。取消状态决定了线程是否可以被取消。如果取消状态被设置为 PTHREAD_CANCEL_ENABLE,则线程可以被取消;如果取消状态被设置为 PTHREAD_CANCEL_DISABLE,则线程不会被取消。
当线程被取消时,会根据取消类型的设置来决定线程的行为。取消类型可以通过 pthread_setcanceltype 函数设置。
6.例子
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
pthread_t tid[2];
//void *类型的函数可以没有显示的返回值
void *my_thread1(void *arg)
{
for(int i = 0; i < 3; i++)
{
printf("this is my_thread1\n");
sleep(1);
}
//取消线程2
pthread_cancel(tid[1]);
//线程退出
// pthread_exit((void *)100);
return (void *)100;
}
void *my_thread2(void *arg)
{
//修改属性,不能被取消
int old;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
for(int i = 0; i < 5; i++)
{
printf("%s\n", (char *)arg);
sleep(1);
}
}
int main()
{
//1.线程号 2.线程属性 3.线程函数 3.线程函数参数
if(pthread_create(&tid[0], NULL, my_thread1, NULL) != 0)
{
perror("pthread_create");
exit(1);
}
//虽然 "helloworld" 是一个字符串常量,但它在 C 语言中被视为字符数组的首地址,
//因此可以将其传递给 pthread_create 函数作为参数。
if(pthread_create(&tid[1], NULL, my_thread2, "helloworld") != 0)
{
perror("ptherad_create");
exit(2);
}
//主线程一定不能提前结束
//主线程等待,直到两个线程都结束
void *status;
pthread_join(tid[0], &status); //等待子线程结束,回收线程资源
printf("线程1结束: %d\n", (int)status);
pthread_join(tid[1], &status);
printf("线程2结束\n");
return 0;
}
二、多线程的实例(两个进程间的接收于发送)
include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#define MSGKEY 1000
pthread_t tid[2];
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
void *recv_thread(void *arg)
{
int msgid = *(int *)arg;
struct msgbuf m;
while(1)
{
if(msgrcv(msgid, &m, sizeof(m.mtext), 2, 0) == -1)
{
perror("msgrcv");
break;
}
if(!strcmp(m.mtext, "bye"))
{
pthread_cancel(tid[1]);
}
printf("%s\n", m.mtext);
memset(&m, 0, sizeof(m));
}
}
void *send_thread(void *arg)
{
int msgid = *(int *)arg;
struct msgbuf m;
while(1)
{
scanf("%s", m.mtext);
m.mtype = 1; //消息类型
if(msgsnd(msgid, &m, sizeof(m.mtext), 0) == -1)
{
perror("msgsnd");
break;
}
if(!strcmp(m.mtext, "bye"))
{
pthread_cancel(tid[0]);
break;
}
memset(&m, 0, sizeof(m));
}
}
int main()
{
//创建消息队列,IPC_CREAT 标志用于创建新的消息队列,这里使用了 IPC_EXCL 标志,如果消息队列已存在,则返回错误。
int msgid = msgget(MSGKEY, IPC_CREAT | IPC_EXCL);
if(-1 == msgid)
{
perror("msgget");
exit(1);
}
if(pthread_create(&tid[0], NULL, recv_thread, &msgid) != 0)
{
perror("ptherad_create");
exit(2);
}
if(pthread_create(&tid[1], NULL, send_thread, &msgid) != 0)
{
perror("pthread_create");
exit(3);
}
void *status;
pthread_join(tid[0], &status);
pthread_join(tid[1], &status);
//删除消息队列
msgctl(msgid, IPC_RMID, NULL);
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#define MSGKEY 1000
pthread_t tid[2] = {0};
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
void *recv_thread(void *arg)
{
int msgid = *(int *)arg;
struct msgbuf m;
while(1)
{
if(msgrcv(msgid, &m, sizeof(m.mtext), 1, 0) == -1)
{
perror("msgrcv");
break;
}
if(!strcmp(m.mtext, "bye"))
{
pthread_cancel(tid[1]);
break;
}
printf("%s\n", m.mtext);
memset(&m, 0, sizeof(m));
}
}
void *send_thread(void *arg)
{
int msgid = *(int *)arg;
struct msgbuf m;
while(1)
{
scanf("%s", m.mtext);
m.mtype = 2; //消息类型
if(msgsnd(msgid, &m, sizeof(m.mtext), 0) == -1)
{
perror("msgsnd");
break;
}
if(!strcmp(m.mtext, "bye"))
{
pthread_cancel(tid[0]);
break;
}
memset(&m, 0, sizeof(m));
}
}
int main()
{
//获取消息队列
int msgid = msgget(MSGKEY, 0);
if(-1 == msgid)
{
perror("msgget");
exit(1);
}
if(pthread_create(&tid[0], NULL, recv_thread, &msgid) != 0)
{
perror("ptherad_create");
exit(2);
}
if(pthread_create(&tid[1], NULL, send_thread, &msgid) != 0)
{
perror("pthread_create");
exit(3);
}
void *status;
pthread_join(tid[0], &status);
pthread_join(tid[1], &status);
return 0;
}
三、互斥锁
互斥锁的使用步骤:
- 定义互斥锁变量:
pthread_mutex_t mutex;
- 初始化互斥锁:
//静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//动态初始化
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
- 在需要保护共享资源的临界区代码段前调用 pthread_mutex_lock
pthread_mutex_lock(&mutex);
- 执行临界区代码,对共享资源进行操作。
- 在临界区代码执行完毕后,调用 pthread_mutex_unlock 释放互斥锁:
pthread_mutex_unlock(&mutex);
- 最后,销毁互斥锁:
pthread_mutex_destroy(&mutex);
注意: 互斥锁的初始化、使用和销毁必须成对出现,以确保正确的互斥访问和资源释放。
例子:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
pthread_mutex_t mutex; //定义互斥锁
int g_ticket = 100;
//自定义延时函数
void delay()
{
for(int i = 0; i < 10000; i++)
for(int j = 0; j < 5000; j++);
}
void *sale_ticket(void *arg)
{
int cur;
while(1)
{
pthread_mutex_lock(&mutex);
cur = g_ticket;
if(cur <= 0)
{
pthread_mutex_unlock(&mutex);
break;
}
printf("%ld get %d\n", pthread_self(), cur);
cur--;
g_ticket = cur;
pthread_mutex_unlock(&mutex);
delay();
}
}
int main()
{
pthread_mutex_init(&mutex, NULL);
pthread_t tid[5];
for(int i = 0; i < 5; i++)
if(pthread_create(&tid[i], NULL, sale_ticket, NULL) != 0)
{
perror("pthread_create");
break;
}
void *status;
for(int i = 0; i < 5; i++)
{
pthread_join(tid[i], &status);
}
pthread_mutex_destroy(&mutex);
return 0;
}
四、条件变量
-
条件变量(Condition Variable)是一种线程同步机制,用于在多线程编程中实现线程间的等待和通知机制。条件变量允许线程在某个条件满足时等待,同时允许其他线程在条件满足时通知等待的线程继续执行。
-
条件变量通常与互斥锁(Mutex)结合使用,以实现更复杂的线程同步操作。互斥锁用于保护共享资源,而条件变量用于在线程等待共享资源的条件不满足时进行等待,以及在条件满足时通知等待的线程。
-
在 POSIX 线程库中,条件变量由 pthread_cond_t 类型表示。使用条件变量时,一般需要配合互斥锁一起使用,以确保线程等待和通知的正确性。
条件变量的基本操作包括:
- 初始化条件变量:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
- 等待条件满足:
pthread_cond_wait(&cond, &mutex);
该函数会使当前线程进入等待状态,直到条件变量 cond 被其他线程发出的信号(通知)唤醒。在调用 pthread_cond_wait 之前,需要先获取互斥锁 mutex,以保证线程等待的正确性。
- 发出信号(通知):
pthread_cond_signal(&cond);
该函数用于向等待在条件变量 cond 上的线程发出一个信号,通知其中的一个线程可以继续执行。被通知的线程会从等待状态中被唤醒,并尝试重新获取互斥锁 mutex。
- 广播信号(广播通知):
pthread_cond_broadcast(&cond);
该函数用于向等待在条件变量 cond 上的所有线程发出广播信号,通知它们可以继续执行。所有等待的线程都会被唤醒,并尝试重新获取互斥锁 mutex。
例子:
include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
pthread_mutex_t mutex; //定义互斥锁
pthread_cond_t cond; //第一条件变量
int g_ticket = 100;
//自定义延时函数
void delay()
{
for(int i = 0; i < 10000; i++)
for(int j = 0; j < 5000; j++);
}
void *sale_ticket_a(void *arg)
{
int cur;
while(1)
{
pthread_mutex_lock(&mutex);
cur = g_ticket;
if(cur <= 0)
{
pthread_mutex_unlock(&mutex);
break;
}
//如果票数等于50,唤醒b
if(cur == 50)
{
pthread_cond_signal(&cond);
}
printf("%ld get %d\n", pthread_self(), cur);
cur--;
g_ticket = cur;
pthread_mutex_unlock(&mutex);
delay();
}
}
void *sale_ticket_b(void *arg)
{
int cur;
while(1)
{
pthread_mutex_lock(&mutex);
cur = g_ticket;
if(cur <= 0)
{
pthread_mutex_unlock(&mutex);
break;
}
//如果票数大于50,睡眠
if(cur > 50)
{
pthread_cond_wait(&cond, &mutex);
cur = g_ticket;
}
printf("%ld get %d\n", pthread_self(), cur);
cur--;
g_ticket = cur;
pthread_mutex_unlock(&mutex);
delay();
}
}
int main()
{
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
pthread_t tid[2];
if(pthread_create(&tid[0], NULL, sale_ticket_a, NULL) != 0)
{
perror("pthread_create");
exit(1);
}
if(pthread_create(&tid[1], NULL, sale_ticket_b, NULL) != 0)
{
perror("pthread_create");
exit(2);
}
void *status;
for(int i = 0; i < 2; i++)
{
pthread_join(tid[i], &status);
}
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
线程池的实现
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
//表示任务队列结点的结构体
typedef struct Task
{
void (*function)(void *arg);
void *arg;
struct Task *next;
}Task;
//表示线程池
typedef struct ThreadPool
{
//任务队列
Task *queueFront;
Task *queueRear;
//线程的数量
int num;
//线程号
pthread_t *threadID;
//互斥锁和条件变量
pthread_mutex_t mutex;
pthread_cond_t cond;
//关闭线程池的标志位
int shutdown;
}ThreadPool;
//线程处理函数
void *worker(void *arg)
{
ThreadPool *pool = (ThreadPool *)arg;
while (1)
{
pthread_mutex_lock(&pool->mutex);
//如果任务队列为空 且线程池没有被关闭 线程睡眠
while (pool->queueFront == pool->queueRear && pool->shutdown == 0)
{
pthread_cond_wait(&pool->cond, &pool->mutex);
}
//如果线程池被关闭
if (pool->shutdown == 1)
{
pthread_mutex_unlock(&pool->mutex);
printf("线城池被关闭 线程 %ld 退出...\n", pthread_self());
pthread_exit((void *)0);
}
//从任务队列获取(出队)一个任务,并且执行
Task task;
Task *t = pool->queueFront->next;
task.function = t->function;
task.arg = t->arg;
pool->queueFront->next = t->next;
free(t);
if (pool->queueFront->next == NULL)
{
pool->queueRear = pool->queueFront;
}
//释放互斥锁
pthread_mutex_unlock(&pool->mutex);
//执行任务
printf("thread %ld start working ...\n", pthread_self());
task.function(task.arg); //通过函数指针调用函数
printf("thread %ld end working ...\n", pthread_self());
}
}
ThreadPool *create_thread_pool(int num)
{
//申请线程池结构体
ThreadPool *pool = (ThreadPool *)malloc(sizeof(ThreadPool));
if (NULL == pool)
{
fprintf(stderr, "malloc ThreadPool failure\n");
return NULL;
}
//初始化任务队列
pool->queueFront = (Task *)malloc(sizeof(Task));
if (NULL == pool->queueFront)
{
fprintf(stderr, "malloc Task failure\n");
free(pool);
return NULL;
}
pool->queueRear = pool->queueFront;
pool->queueFront->next = NULL;
//初始化线程的数量
pool->num = num;
//初始化线程号
pool->threadID = (pthread_t *)malloc(sizeof(pthread_t) * num);
if (NULL == pool->threadID)
{
fprintf(stderr, "malloc pthread_t failure\n");
free(pool->queueFront);
free(pool);
return NULL;
}
//初始化线程
int i;
for (i = 0; i < num; i++)
{
if (pthread_create(&pool->threadID[i], NULL, worker, pool) != 0)
{
fprintf(stderr, "pthread_create failure\n");
free(pool->queueFront);
free(pool->threadID);
free(pool);
return NULL;
}
pthread_detach(pool->threadID[i]); //线程运行结束后自动释放资源
}
//初始化互斥锁和条件变量
pthread_mutex_init(&pool->mutex, NULL);
pthread_cond_init(&pool->cond, NULL);
//初始化关闭线程池的标志位
pool->shutdown = 0;
return pool;
}
//任务函数
void taskfunc(void *arg)
{
int num = *(int *)arg;
printf("thread %ld is working num = %d ...\n", pthread_self(), num);
sleep(1);
free(arg);
}
void thread_pool_add(ThreadPool *pool, void (*func)(void *), void *arg)
{
pthread_mutex_lock(&pool->mutex);
//进队操作
Task *t = (Task *)malloc(sizeof(Task));
if (NULL == t)
{
fprintf(stderr, "malloc Task failure\n");
return;
}
t->function = func;
t->arg = arg;
t->next = NULL;
pool->queueRear->next = t;
pool->queueRear = t;
pthread_mutex_unlock(&pool->mutex);
pthread_cond_signal(&pool->cond);
}
void thread_pool_destroy(ThreadPool *pool)
{
//关闭线程池
pool->shutdown = 1;
//唤醒所有线程
int i;
for (i = 0; i < pool->num; i++)
{
pthread_cond_signal(&pool->cond);
}
//释放线程号
if (pool->threadID)
free(pool->threadID);
//释放任务队列
while (pool->queueFront->next)
{
Task *t = pool->queueFront->next;
pool->queueFront->next = t->next;
free(t);
}
free(pool->queueFront);
//销毁互斥量和条件变量
pthread_mutex_destroy(&pool->mutex);
pthread_cond_destroy(&pool->cond);
//释放线程池结构体
free(pool);
}
int main()
{
//创建线程池
ThreadPool *pool = create_thread_pool(10);
if (NULL == pool)
{
return -1;
}
printf("线程池创建完成 \n");
sleep(1);
//主线程往任务队列中添加任务,并且唤醒线程池中的线程
int i;
for (i = 0; i < 50; i++)
{
int *n = (int *)malloc(sizeof(int));
*n = i;
//把任务添加到任务队列
thread_pool_add(pool, taskfunc, n);
}
sleep(6);
thread_pool_destroy(pool);
return 0;
}
分析以上代码