【C语言】线程同步

【C语言】线程同步

  • 线程同步
    • [1. 互斥锁](#1. 互斥锁)
    • [2. 读写锁](#2. 读写锁)
    • [3. 条件变量](#3. 条件变量)
    • [4. 信号量](#4. 信号量)

线程同步

线程同步是指在多线程的情况下,如果多个线程去访问共享资源,需要按照一定规则顺序依次去访问,保证共享资源的数据一致性。

1. 互斥锁

互斥相关函数

c 复制代码
//互斥量
pthread_mutex_t mutex;

//pthread_mutex_init()
//互斥量初始化
int pthread_mutex_init(pthread_mutex_t *restrict mutex, 
						        const pthread_mutexattr_t *restrict attr);
参数
    mutex 传出参数
    attr 互斥量属性,通常设为NULL,线程间共享 

//pthread_mutex_lock()
//互斥量加锁
int pthread_mutex_lock(pthread_mutex_t *mutex);

//pthread_mutex_unlock()
//互斥量解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);

//pthread_mutex_trylock
//尝试加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);

//pthread_mutex_destroy()
//销毁互斥量
int pthread_mutex_destroy(pthread_mutex_t *mutex);

示例:

多线程访问全局变量num,并实现同步。

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

int num = 0;
pthread_mutex_t mutex;

void *threadMethod1(void *arg)
{
    int i, cur;
    while (num<5000)
    {
        pthread_mutex_lock(&mutex);
        if (num == 5000)
        {
            break;
        }
        cur = num;
        cur++;
        num = cur;

        printf("[%ld] num [%d]\n", pthread_self(), num);
        pthread_mutex_unlock(&mutex);
    }
}

void *threadMethod2(void *arg)
{
    int i, cur;

    while (num<5000)
    {
        pthread_mutex_lock(&mutex);
        if (num == 5000)
        {
            break;
        }
        
        cur = num;
        cur++;
        num = cur;

        printf("[%ld] num [%d]\n", pthread_self(), num);
        pthread_mutex_unlock(&mutex);
    }

    // for (i = 0; i < 5000; i++)
    // {
    //     pthread_mutex_lock(&mutex);
    //     cur = num;
    //     cur++;
    //     num = cur;

    //     printf("[%ld] num [%d]\n", pthread_self(), num);
    //     pthread_mutex_unlock(&mutex);
    // }
}

int main()
{

    pthread_mutex_init(&mutex, NULL);
    pthread_t thread1, thread2;

    int ret = pthread_create(&thread1, NULL, threadMethod1, NULL);
    if (ret != 0)
    {
        printf("pthread_create error, [%s]\n", strerror(ret));
        return -1;
    }
    ret = pthread_create(&thread2, NULL, threadMethod1, NULL);
    if (ret != 0)
    {
        printf("pthread_create error, [%s]\n", strerror(ret));
        return -1;
    }

    void *exit_status;
    ret = pthread_join(thread1, &exit_status);
    if (ret)
    {
        printf("Thread join error: %d\n", ret);
        return -1;
    }
    ret = pthread_join(thread2, &exit_status);
    if (ret)
    {
        printf("Thread join error: %d\n", ret);
        return -1;
    }

    printf("main thread, pid is [%d], tid is [%ld]\n", getpid(), pthread_self());
    pthread_mutex_destroy(&mutex);

    return 0;
}

2. 读写锁

读共享,写独占,适用于读多余写的情况。读写同时等待时,写的优先级更高。

读写锁相关函数:

c 复制代码
//1. 读写锁变量
pthread_rwlock_t rwlock;
//2. 读写锁初始化,参数attr为读写锁属性,设置为NULL表示设置为默认属性
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
//3. 加读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
//4.尝试加读锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
//5. 加写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
//6. 尝试加写锁
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
//7.解锁
int pthread_rwlock_unlock(&pthread_rwlock_t *rwlock);
//8. 销毁锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);        

示例:

5个读线程,3个写线程来访问共享资源num

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

#define FINALNUM 100
int num = 0;
pthread_rwlock_t rdlock;

// 读线程
void *threadMethod1(void *arg)
{
    int cur;
    while (num<FINALNUM)
    {
        pthread_rwlock_rdlock(&rdlock);
        if (num > FINALNUM)
        {
            pthread_rwlock_unlock(&rdlock);
            break;
        }
        
        cur = num;

        printf("read [%d] : [%ld] num [%d]\n",*(int *)arg, pthread_self(), cur);
        pthread_rwlock_unlock(&rdlock);  
        sleep(rand()%3);
    }
}
// 写线程
void *threadMethod2(void *arg)
{
    int cur;

    while (num < FINALNUM)
    {
        pthread_rwlock_wrlock(&rdlock);
        if (num == FINALNUM)
        {
            pthread_rwlock_unlock(&rdlock);
            break;
        }

        cur = num;
        cur++;
        num = cur;

        printf("write [%d] : [%ld] num [%d]\n",*(int *)arg, pthread_self(), cur);
        pthread_rwlock_unlock(&rdlock);
        sleep(rand()%3);
    }
}

int main()
{

    pthread_rwlock_init(&rdlock, NULL);
    pthread_t threadr[5], threadw[3];
    int i = 0;
    int ret;
    int arr[8];
    // 5个读线程
    for (i = 0; i < 5; i++)
    {
        arr[i] = i + 1;
        ret = pthread_create(&threadr[i], NULL, threadMethod1, &arr[i]);
        if (ret != 0)
        {
            printf("pthread_create read [%d] error, [%s]\n", i+1, strerror(ret));
            return -1;
        }
    }
    // 3个写线程
    for (i = 0; i < 3; i++)
    {
        arr[i + 5] = i + 1;
        ret = pthread_create(&threadw[i], NULL, threadMethod2, &arr[i + 5]);
        if (ret != 0)
        {
            printf("pthread_create write [%d] error, [%s]\n", i+1, strerror(ret));
            return -1;
        }
    }

    void *exit_status;

    for (i = 0; i < 5; i++)
    {
        ret = pthread_join(threadr[i], &exit_status);
        if (ret)
        {
            printf("Thread join read [%d] error: [%s]\n", i+1, strerror(ret));
            return -1;
        }
    }

    for (i = 0; i < 3; i++)
    {
        ret = pthread_join(threadw[i], &exit_status);
        if (ret)
        {
            printf("Thread join write [%d] error: [%s]\n", i+1, strerror(ret));
            return -1;
        }
    }

    printf("main thread, pid is [%d], tid is [%ld]\n", getpid(), pthread_self());
    pthread_rwlock_destroy(&rdlock);
    printf("here is main ,num final is [%d]\n",num);

    return 0;
}
//输出
...
read [5] : [140482278684416] num [96]
read [5] : [140482278684416] num [96]
read [3] : [140482295469824] num [96]
write [3] : [140482253506304] num [97]
write [3] : [140482253506304] num [98]
write [3] : [140482253506304] num [99]
read [1] : [140482312255232] num [99]
read [2] : [140482303862528] num [99]
read [4] : [140482287077120] num [99]
write [2] : [140482261899008] num [100]
main thread, pid is [3151], tid is [140482320643840]
here is main ,num final is [100]

3. 条件变量

条件变量不是锁,但是不满足,线程阻塞,释放互斥锁。条件满足,通知所有阻塞的线程去争夺互斥锁。

条件变量相关函数

c 复制代码
//1.定义一个条件变量
pthread_cond_t  cond;
//2.条件变量初始化,成功返回0, 失败返回错误编号
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
参数: 
	cond: 条件变量
	attr: 条件变量属性, 传NULL表示默认属性

//2.条件不满足, 引起线程阻塞并解锁;条件满足, 解除线程阻塞, 并加锁。成功返回0, 失败返回错误号
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);     
参数:
	cond: 条件变量
	mutex: 互斥锁变量
//3.唤醒至少一个阻塞在该条件变量上的线程,成功返回0, 失败返回错误号。
int pthread_cond_signal(pthread_cond_t *cond);

//4.销毁条件变量,成功返回0, 失败返回错误编号
int pthread_cond_destroy(pthread_cond_t *cond);

示例:

多线程模拟生产者消费者模型,并且设置条件变量进行线程同步

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>

typedef struct linklist
{
	int num;
	struct linklist *next;
} LINKLIST;

LINKLIST *head = NULL;
pthread_mutex_t mutex;
pthread_cond_t cond;

int len;

void *producter(void *arg)
{
	LINKLIST *node = NULL;
	while (1)
	{

		pthread_mutex_lock(&mutex);
		node = (LINKLIST *)malloc(sizeof(LINKLIST));
		if (node == NULL)
		{
			perror("malloc error");
			exit(-1);
		}
		head->num = head->num + 1;
		len += 1;
		node->num = len;
		node->next = head->next;
		head->next = node;
		printf("linklist len is [%d]\n", len);

		printf("producer [%d] , cur monunt is [%d],get data num [%d]\n", *(int *)arg, head->num, node->num);
		pthread_mutex_unlock(&mutex);
		pthread_cond_signal(&cond);
		sleep(rand() % 3);
	}
}

void *consumer(void *arg)
{
	LINKLIST *temp = NULL;
	while (1)
	{

		pthread_mutex_lock(&mutex);
		printf("consumer [%d] mutex lock\n", *(int *)arg);
		if (head->next == NULL)
		{
			pthread_cond_wait(&cond, &mutex);
			printf("first consumer [%d] cond wait\n", *(int *)arg);
			if (head->next == NULL)
			{
				printf("linklist len is [%d]\n", len);
				pthread_mutex_unlock(&mutex);
				printf("first consumer [%d] mutex unlock\n", *(int *)arg);
				continue;
			}
		}
		temp = head->next;
		
		printf("consumer [%d] , cur monunt is [%d],get data num [%d]\n", *(int *)arg, head->num, temp->num);
		
		head->next = temp->next;
		temp->next =NULL;
		free(temp);
		temp = NULL;
		head->num = head->num - 1;
		len -= 1;
		pthread_mutex_unlock(&mutex);
		printf("consumer [%d] mutex unlock\n", *(int *)arg);

		sleep(rand() % 3);
	}
}

int main()
{
	head = (LINKLIST *)malloc(sizeof(LINKLIST));
	head->num = 0;
	head->next = NULL;
	len = 0;
	pthread_mutex_init(&mutex, NULL);
	pthread_cond_init(&cond, NULL);
	pthread_t products[3], consumers[3];
	int arr[6];
	int ret;
	int i;
	for (i = 0; i < 3; i++)
	{
		arr[i] = i + 1;
		ret = pthread_create(&products[i], NULL, producter, &arr[i]);
		if (ret != 0)
		{
			printf("pthread_create products [%d] error, [%s]\n", i + 1, strerror(ret));
			return -1;
		}
	}

	for (i = 0; i < 3; i++)
	{
		arr[i + 3] = i + 1;
		ret = pthread_create(&consumers[i], NULL, consumer, &arr[i + 3]);
		if (ret != 0)
		{
			printf("pthread_create consumers [%d] error, [%s]\n", i + 1, strerror(ret));
			return -1;
		}
	}

	void *exit_status;
	for (i = 0; i < 3; i++)
	{
		ret = pthread_join(products[i], &exit_status);
		if (ret)
		{
			printf("Thread join products [%d] error: %d\n", i + 1, ret);
			return -1;
		}
	}

	for (i = 0; i < 3; i++)
	{
		ret = pthread_join(consumers[i], &exit_status);
		if (ret)
		{
			printf("Thread join consumers [%d] error: %d\n", i + 1, ret);
			return -1;
		}
	}

	printf("main thread, pid is [%d], tid is [%ld]\n", getpid(), pthread_self());
	pthread_cond_destroy(&cond);
	pthread_mutex_destroy(&mutex);

	return 0;
}

4. 信号量

信号量可以看成是当某个互斥锁有多把的时候,多个线程可以同时访问一个共享资源,当锁不够的时候,就会阻塞。如果锁只有一把,就等同于一个互斥量的情况。

c 复制代码
#include <semaphore.h>
//1. 定义信号量 
sem_t sem;
//2. 初始化信号量,成功返回0, 失败返回-1, 并设置errno值
int sem_init(sem_t *sem, int pshared, unsigned int value);	
参数:
	sem: 信号量变量
	pshared: 0表示线程同步, 1表示进程同步
	value: 最多有几个线程操作共享数据

//2. 调用该函数一次, 相当于sem--, 当sem为0的时候, 引起阻塞
//成功返回0, 失败返回-1, 并设置errno值
int sem_wait(sem_t *sem);

//3. 调用一次, 相当于sem++,成功返回0, 失败返回-1, 并设置errno值
int sem_post(sem_t *sem);
    
//4. 尝试加锁, 若失败直接返回, 不阻塞
//成功返回0, 失败返回-1, 并设置errno值
int sem_trywait(sem_t *sem);

//5. 销毁信号量,成功返回0, 失败返回-1, 并设置errno值
int sem_destroy(sem_t *sem);

示例:

信号量只有一个的时候等同于互斥锁

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>

typedef struct linklist
{
	int num;
	struct linklist *next;
} LINKLIST;

LINKLIST *head = NULL;

// 定义信号量
sem_t sem_producer;
sem_t sem_consumer;

int len;
int tim = 0;

void *producter(void *arg)
{
	LINKLIST *node = NULL;
	while (1)
	{

		sem_wait(&sem_producer);
		node = (LINKLIST *)malloc(sizeof(LINKLIST));
		if (node == NULL)
		{
			perror("malloc error");
			exit(-1);
		}
		head->num = head->num + 1;
		len += 1;
		node->num = len;
		node->next = head->next;
		head->next = node;
		tim++;

		printf("[%d] producer [%d] , cur monunt is [%d],set data num [%d]\n", tim, *(int *)arg, head->num, node->num);
		printf("[%d] linklist len is [%d]\n", tim, len);
		sem_post(&sem_consumer);
		sleep(rand() % 3);
	}
}

void *consumer(void *arg)
{
	LINKLIST *temp = NULL;
	while (1)
	{

		sem_wait(&sem_consumer);
		tim++;
		temp = head->next;

		printf("[%d] consumer [%d] , cur monunt is [%d],get data num [%d]\n", tim, *(int *)arg, head->num, temp->num);

		head->next = temp->next;
		temp->next = NULL;
		free(temp);
		temp = NULL;
		head->num = head->num - 1;
		len -= 1;
		sem_post(&sem_producer);

		sleep(rand() % 3);
	}
}

int main()
{
	head = (LINKLIST *)malloc(sizeof(LINKLIST));
	head->num = 0;
	head->next = NULL;
	len = 0;
	sem_init(&sem_consumer, 0, 0);
	sem_init(&sem_producer, 0, 1);

	pthread_t products[3], consumers[3];
	int arr[6];
	int ret;
	int i;
	for (i = 0; i < 3; i++)
	{
		arr[i] = i + 1;
		ret = pthread_create(&products[i], NULL, producter, &arr[i]);
		if (ret != 0)
		{
			printf("pthread_create products [%d] error, [%s]\n", i + 1, strerror(ret));
			return -1;
		}
	}

	for (i = 0; i < 3; i++)
	{
		arr[i + 3] = i + 1;
		ret = pthread_create(&consumers[i], NULL, consumer, &arr[i + 3]);
		if (ret != 0)
		{
			printf("pthread_create consumers [%d] error, [%s]\n", i + 1, strerror(ret));
			return -1;
		}
	}

	void *exit_status;
	for (i = 0; i < 3; i++)
	{
		ret = pthread_join(products[i], &exit_status);
		if (ret)
		{
			printf("Thread join products [%d] error: %d\n", i + 1, ret);
			return -1;
		}
	}

	for (i = 0; i < 3; i++)
	{
		ret = pthread_join(consumers[i], &exit_status);
		if (ret)
		{
			printf("Thread join consumers [%d] error: %d\n", i + 1, ret);
			return -1;
		}
	}

	printf("main thread, pid is [%d], tid is [%ld]\n", getpid(), pthread_self());
	sem_destroy(&sem_consumer);
	sem_destroy(&sem_producer);

	return 0;
}
相关推荐
鲤籽鲲27 分钟前
C# _ 数字分隔符的使用
开发语言·c#
fillwang1 小时前
Python实现Excel行列转换
开发语言·python·excel
北极糊的狐2 小时前
SQL中,# 和 $ 用于不同的占位符语法
java·开发语言
漫漫不慢.3 小时前
九进制转10进制
java·开发语言
西猫雷婶3 小时前
python学opencv|读取图像(二十五)使用cv2.putText()绘制文字进阶-垂直镜像文字
开发语言·python·opencv
大小科圣3 小时前
windows配置jdk
java·开发语言
西猫雷婶4 小时前
python学opencv|读取图像(二十四)使用cv2.putText()绘制文字进阶-倾斜文字
开发语言·python·opencv
2401_858286115 小时前
L27.【LeetCode笔记】2 的幂(五种解法)
c语言·开发语言·笔记·算法·leetcode
代码对我眨眼睛5 小时前
vite+vue3动态引入资源文件(问题已解决但离了个大谱)
开发语言·javascript·vue.js
疯狂的沙粒5 小时前
如何在 JavaScript 中实现日期格式化?
开发语言·前端·css·node.js