首页 > 其他分享 >【C语言】线程同步

【C语言】线程同步

时间:2024-06-23 22:03:22浏览次数:21  
标签:同步 int ret C语言 num mutex pthread 线程 sem

【C语言】线程同步

线程同步

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

1. 互斥锁

互斥相关函数

//互斥量
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,并实现同步。

#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. 读写锁

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

读写锁相关函数:

//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

#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. 条件变量

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

条件变量相关函数

//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);

示例:

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

#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. 信号量

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

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

示例:

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

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

最后

推荐一个零声教育学习教程,个人觉得老师讲得不错,分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,
TCP/IP,协程,DPDK等技术内容,点击立即学习:链接

标签:同步,int,ret,C语言,num,mutex,pthread,线程,sem
From: https://blog.csdn.net/qq_44653106/article/details/139880542

相关文章

  • python基础 - 多线程技术
    基础概念importtimedeftest(something):print(f"我开始>>>{something}")time.sleep(2)print(f"我结束>>>{something}")"""场景:1-io密集型--阻塞sleeprequestssocket"""importthreading"&quo......
  • C语言从入门到进阶(15万字总结)
    前言:《C语言从入门到进阶》这本书可是作者呕心沥血之作,建议零售价1元,当然这里开个玩笑。 本篇博客可是作者之前写的所有C语言笔记博客的集结,本篇博客不止有知识点,还有一部分代码练习。有人可能会问,作者不会是cv战士吧!作者在这里回答大家,有cv战士的成分,但不完全是。我是将......
  • 【永磁同步电机PMSM直接转矩控制(DTC)Simulink仿真】
    文章目录前言一、直接转矩控制(DTC)是什么?二、三相PMSM传统的DTC框图三、Simulink仿真模型搭建前言本章首先介绍永磁同步电机传统直接转矩控制(DirectTorqueControl,DTC)的基本工作原理和实现方法,然后搭建仿真模型并给出仿真结果。后续为了改善传统DTC存在的缺......
  • C语言中操作符详解(二)
    OK,今天继续为诸君带来有关C语言中操作符的讲解一.位操作符 C语言中的位操作符我相信大家并不陌生,我们在之前就已经接触过了一些位操作符(位操作符的操作数只能是整数):(1)&:按位与(同为1才为1,否则为0)(2)| :按位或(有1就为1)(3)^:按位异或(不同为1,否则为0)(4)~:按位......
  • 文件操作(1)(C语言版)
    前言:            为什么要学习文件操作:    1、如果大家写过一些代码,当运行结束的时候,这些运行结果将不复存在,除非,再次运行时这些结果才能展现在屏幕上面,就比如之前写过的通讯录。    现实中的通讯录可以保存自己写进去的信息,下次方便查找,......
  • 【七】【QT开发应用】跨UI发送信号,跨线程发送信号
    跨UI发送信号基本框架新建窗口自定义信号跨线程发送信号新建线程查看线程号完整代码跨UI发送信号setdialog.h#ifndefSETDIALOG_H#defineSETDIALOG_H#include<QDialog>namespaceUi{classsetdialog;}class......
  • 线程间通信方式
    1通信机制:互斥与同步线程的互斥通过线程的互斥锁完成;线程的同步通过无名信号量或者条件变量完成。2 互斥2.1何为互斥?       互斥是在多个线程在访问同一个全局变量的时候,先让这个线程争抢锁的资源,那个线程争抢到资源,它可以访问这个变量,没有争抢到资源的线程不......
  • 如何学习C语言
    学习三阶段:初识——>初阶——>进阶注:蓝色字体皆可跳转一阶:初识1.基本了解C语言的基础知识,对C语言有一个大概的认识2.简单认识每个知识点,后期在初阶和进阶进行详细描述学习内容1.什么是C语言2.第一个C语言程序3.数据类型4.变量、常量5.字符串、转义字符、注释......
  • VScode配置C语言开发环境
    可以总结为两步1.本地安装和配置编译器2.VScode上安装C/C++扩展最后直接运行即可安装和配置编译器Windows系统可以使用mingw64,发布网址在这里https://sourceforge.net/projects/mingw-w64/files/这是压缩包格式,可以自由选择目录解压,比如我把mingw64放在D盘根目录在命......
  • 【2024最新精简版】线程安全/多线程 面试篇
    文章目录一.线程基础线程和进程什么是进程什么是线程并发与并行的区别创建线程继承Thread类实现Runable接口实现Callable接口使用线程池线程状态等待唤醒机制等待方法唤醒方法二.线程池线程池作用创建线程池线程池任务调度流程阻塞队列BlockingQueue线程池拒绝策......