Linux下的线程操作

一、多线程的创建于退出

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:指向线程函数的指针,该函数用于执行线程的主要逻辑。
  • arg:传递给线程函数的参数,可以是任意类型的指针。

pthread_create 函数会创建一个新的线程,并在新线程中执行指定的线程函数 start_routine。线程函数的参数可以通过 arg 传递。

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                                                                                                                                   
#include 
#include 
#include 

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 
#include 
#include 
#include 
#include 
#include 

#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                                                                                                                                   
#include 
#include 
#include 
#include 
#include 
#include 

#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;   
}             

三、互斥锁

互斥锁的使用步骤:

  1. 定义互斥锁变量:
pthread_mutex_t mutex;
  1. 初始化互斥锁:
//静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//动态初始化
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
  1. 在需要保护共享资源的临界区代码段前调用 pthread_mutex_lock
pthread_mutex_lock(&mutex);
  1. 执行临界区代码,对共享资源进行操作。
  2. 在临界区代码执行完毕后,调用 pthread_mutex_unlock 释放互斥锁:
pthread_mutex_unlock(&mutex);
  1. 最后,销毁互斥锁:
pthread_mutex_destroy(&mutex);

注意: 互斥锁的初始化、使用和销毁必须成对出现,以确保正确的互斥访问和资源释放。

例子:

#include                                                                                                                                   
#include 
#include 

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 类型表示。使用条件变量时,一般需要配合互斥锁一起使用,以确保线程等待和通知的正确性。

条件变量的基本操作包括:

  1. 初始化条件变量:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

  1. 等待条件满足:
pthread_cond_wait(&cond, &mutex);

该函数会使当前线程进入等待状态,直到条件变量 cond 被其他线程发出的信号(通知)唤醒。在调用 pthread_cond_wait 之前,需要先获取互斥锁 mutex,以保证线程等待的正确性。

  1. 发出信号(通知):
pthread_cond_signal(&cond);

该函数用于向等待在条件变量 cond 上的线程发出一个信号,通知其中的一个线程可以继续执行。被通知的线程会从等待状态中被唤醒,并尝试重新获取互斥锁 mutex。

  1. 广播信号(广播通知):
pthread_cond_broadcast(&cond);

该函数用于向等待在条件变量 cond 上的所有线程发出广播信号,通知它们可以继续执行。所有等待的线程都会被唤醒,并尝试重新获取互斥锁 mutex。

例子:

include <stdio.h>                                                                                                                                  
#include 
#include 

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 
#include 
#include 
#include 

//表示任务队列结点的结构体
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;
}
分析以上代码

你可能感兴趣的:(linux,c++,c语言)