LinuxC高级编程——线程间同步

 LinuxC高级编程——线程间同步


宗旨:技术的学习是有限的,分享的精神是无限的。


1、 互斥锁mutex

        多个线程同时访问共享数据时可能会冲突。对于多线程的程序,访问冲突的问题是很普遍的,解决的办法是引入互斥锁(Mutex, Mutual Exclusive Lock),获得锁的线程可以完成“读-修改-写”的操作,然后释放锁给其它线程,没有获得 锁的线程只能等待而不能访问共享数据,这样“读-修改-写”三步操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其它处理器上并行做这个操作。Mutex用pthread_mutex_t类型的变量表示,可以这样初始化和销毁:

#include<pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,constpthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

        返回值:成功返回0,失败返回错误号。

pthread_mutex_init函数对Mutex做初始化,参数attr设定Mutex的属性,如果attr为NULL则表示缺省属性,用pthread_mutex_init函 数初始化的Mutex可以用pthread_mutex_destroy销毁。如果Mutex变量是静态分配的(全局变量 或static变量),也可以用宏定义PTHREAD_MUTEX_INITIALIZER来初始化,相当于用pthread_mutex_init初始化并且attr参数为NULL。 Mutex的加锁和解锁操作可以用下列函数:

#include<pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

        返回值:成功返回0,失败返回错误号。

        一个线程可以调用pthread_mutex_lock获得Mutex,如果这时另一个线程已经调用pthread_mutex_lock获得了该Mutex,则当前线程需要挂起等待,直到另一个线程调用pthread_mutex_unlock释放Mutex,当前线程被唤醒,才能获得该Mutex并继续执行。 如果一个线程既想获得锁,又不想挂起等待,可以调用pthread_mutex_trylock,如果Mutex已经被另一个线程获得,这个函数会失败返回EBUSY,而不会使线程挂起等待。 

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>#define NLOOP 5000
int counter; /* incremented by threads */pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;void *doit(void *);int main(int argc, char **argv)
{pthread_t tidA, tidB;pthread_create(&tidA, NULL, doit, NULL);pthread_create(&tidB, NULL, doit, NULL);/* wait for both threads to terminate */pthread_join(tidA, NULL);pthread_join(tidB, NULL);return 0;
}
void *doit(void *vptr)
{int i, val;for (i = 0; i < NLOOP; i++){pthread_mutex_lock(&counter_mutex);val = counter;printf("%x: %d\n", (unsigned int)pthread_self(), val   + 1);counter = val + 1;pthread_mutex_unlock(&counter_mutex);}return NULL;
}

2、 条件变量

        线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行。 在pthread库中通过条件变量(Condition Variable) 来阻塞等待一个条件,或者唤醒等待这个条件的线程。 Condition Variable用pthread_cond_t类型的变量表示,可以这样初始化和销毁:

#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t*restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

        返回值:成功返回0,失败返回错误号。

        和Mutex的初始化和销毁类似,pthread_cond_init函数初始化一个Condition Variable, attr参数 为NULL则表示缺省属性, pthread_cond_destroy函数销毁一个Condition Variable。如果Condition Variable是静态分配的,也可以用宏定义PTHEAD_COND_INITIALIZER初始化,相当于 用pthread_cond_init函数初始化并且attr参数为NULL。 Condition Variable的操作可以用下列函数:

#include <pthread.h>
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t*restrict mutex,
const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrictmutex);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);

        返回值:成功返回0,失败返回错误号。 可见,一个ConditionVariable总是和一个Mutex搭配使用的。一个线程可以调 用pthread_cond_wait在一个Condition Variable上阻塞等待,这个函数做以下三步操作:

1.  释放Mutex   2. 阻塞等待   3. 当被唤醒时,重新获得Mutex并返回.

        pthread_cond_timedwait函数还有一个额外的参数可以设定等待超时,如果到达了abstime所指定的 时刻仍然没有别的线程来唤醒当前线程,就返回ETIMEDOUT。一个线程可以调 用pthread_cond_signal唤醒在某个Condition Variable上等待的另一个线程,也可以调用pthread_cond_broadcast唤醒在这个Condition Variable上等待的所有线程。 

#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>struct msg
{struct msg *next;int num;
};struct msg *head;
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;void *consumer(void *p)
{struct msg *mp;for (;;){pthread_mutex_lock(&lock);while (head == NULL){pthread_cond_wait(&has_product, &lock);}mp = head;head = mp->next;pthread_mutex_unlock(&lock);printf("Consume %d\n", mp->num);free(mp);sleep(rand() % 5);}
}
void *producer(void *p)
{struct msg *mp;for (;;){mp = malloc(sizeof(struct msg));mp->num = rand() % 1000 + 1;printf("Produce %d\n", mp->num);pthread_mutex_lock(&lock);mp->next = head;head = mp;pthread_mutex_unlock(&lock);pthread_cond_signal(&has_product);sleep(rand() % 5);}
}int main(int argc, char *argv[])
{pthread_t pid, cid;srand(time(NULL));pthread_create(&pid, NULL, producer, NULL);pthread_create(&cid, NULL, consumer, NULL);pthread_join(pid, NULL);pthread_join(cid, NULL);return 0;
}

3、 信号量

        Mutex变量是非0即1的,可看作一种资源的可用数量,初始化时Mutex是1,表示有一个可用资源,加锁时获得该资源,将Mutex减到0,表示不再有可用资源,解锁时释放该资源,将Mutex重新加到1,表示又有了一个可用资源。信号量(Semaphore)和Mutex类似,表示可用资源的数量,和Mutex不同的是这个数量可以大于1.

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_post(sem_t * sem);
int sem_destroy(sem_t * sem);

        semaphore变量的类型为sem_t, sem_init()初始化一个semaphore变量, value参数表示可用资源 的数量, pshared参数为0表示信号量用于同一进程的线程间同步,本节只介绍这种情况。在用 完semaphore变量之后应该调用sem_destroy()释放与semaphore相关的资源。 调用sem_wait()可以获得资源,使semaphore的值减1,如果调用sem_wait()时semaphore的值已 经是0,则挂起等待。如果不希望挂起等待,可以调用sem_trywait()。调用sem_post()可以释放资 源,使semaphore的值加1,同时唤醒挂起等待的线程。

#include <stdlib.h>
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>#define NUM 5
int queue[NUM];
sem_t blank_number, product_number;void *producer(void *arg)
{int p = 0;while (1){sem_wait(&blank_number);queue[p] = rand() % 1000 + 1;printf("Produce %d\n", queue[p]);sem_post(&product_number);p = (p + 1) % NUM;sleep(rand() % 5);}
}void *consumer(void *arg)
{int c = 0;while (1){sem_wait(&product_number);printf("Consume %d\n", queue[c]);queue[c] = 0;sem_post(&blank_number);c = (c + 1) % NUM;sleep(rand() % 5);}
}int main(int argc, char *argv[])
{pthread_t pid, cid;sem_init(&blank_number, 0, NUM);sem_init(&product_number, 0, 0);pthread_create(&pid, NULL, producer, NULL);pthread_create(&cid, NULL, consumer, NULL);pthread_join(pid, NULL);pthread_join(cid, NULL);sem_destroy(&blank_number);sem_destroy(&product_number);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/466710.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2021年,这是你们收到的第一份礼物

一、 前言大家好&#xff0c;2020年就要过去了&#xff0c;这一年来&#xff0c;感谢大家对公众号的支持&#xff0c;但是感谢不能停留在嘴上&#xff0c;所以&#xff0c;这次邀请了正点原子赞助。一起给大家送点礼品&#xff01;作为一名 电子/嵌入式 人&#xff0c;正点原子…

SQL SERVER自定义函数

SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- -- Author: captain -- Create date: 2008.05.05 -- Description: 删除垃圾代码 -- ALTER FUNCTION [fzdongmancn].[fun_deleteLj] ( old varchar(1000) ) RETURNS varchar(1000) AS BEGIN declare ind…

Python参考手册(第4版)pdf

下载地址&#xff1a;网盘下载内容简介 本书是权威的Python语言参考指南&#xff0c;内容涉及核心Python语言和Python库的最重要部分。本书内容简洁扼要、可读性强&#xff0c;书中还包括了一些没有在Python官方文档或其他资料中出现过的一些高级的主题。这一版在内容上进…

大数据学习-python通过Pyhive连接hive数据库

1.hbase和hive结合 &#xff08;1&#xff09;hbase建表添加数据 #test是表名&#xff0c;name是列族 #hbase可以一个列族里边多个字段 create test,name#添加数据 put test,1,name:t1,1 put test,1,name:t2,2#查询 scan test#查询 get 表名&#xff0c;row-key&#xff0c;列…

深入理解Linux内核进程上下文切换

在原作者基础上修改了些文字描述&#xff0c;让文章更加通俗易懂作者简介韩传华&#xff0c;就职于南京大鱼半导体有限公司&#xff0c;主要从事linux相关系统软件开发工作&#xff0c;负责Soc芯片BringUp及系统软件开发&#xff0c;乐于分享喜欢学习&#xff0c;喜欢专研Linux…

Linux C高级编程——网络编程基础(1)

Linux高级编程——BSD socket的网络编程 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 一网络通信基础 TCP/IP协议簇基础&#xff1a;之所以称TCP/IP是一个协议簇&#xff0c;是因为TCP/IP包含TCP 、IP、UDP、ICMP等多种协议。下图是OSI模型与TCP/IP模…

使用SQLDMO中“接口SQLDMO.Namelist 的 QueryInterface 失败”异常的解决方法

SQLDMO&#xff08;SQL Distributed Management Objects&#xff0c;SQL分布式管理对象&#xff09;&#xff0c;它封装 Microsoft SQL Server 数据库中的对象。它允许我们通过COM对象&#xff0c;对SQLServer进行管理。SQLDMO对象来自SQLDMO.dll。因为SQLDMO.dll是一个COM对象…

GetKeyState(), GetAsyncKeystate(), GetKeyboardSlate()

GetKeyState、GetAsyncKeyState、GetKeyboardState函数的差别&#xff1a; 1、BOOL GetKeyboardState( PBYTE lpKeyState );获得全部的256个键&#xff08;键盘按键、鼠标按键等等&#xff09;的状态&#xff0c;lpKeyState是指向一个256bit的数组。存放全部键的状态。 2、SHOR…

你月薪多少?

今天跟一个读友聊天&#xff0c;他刚毕业没多久&#xff0c;在一家方案公司上班&#xff0c;月薪6.5&#xff0c;做的Android 开发&#xff0c;用RK方案&#xff0c;有时候也需要做MCU相关的开发&#xff0c;事情多&#xff0c;压力也不少&#xff0c;而且不是双休&#xff0c;…

cx_Oracle安装出现的问题

1.安装cx_Oracle &#xff08;1&#xff09;在线安装 pip install cx_Oracle &#xff08;2&#xff09;源码安装 下载源码&#xff1a;https://pypi.org/project/cx-Oracle/ tar -zxvf cx_Oracle-8.1.0.tar.gz cd cx_Oracle-8.1.0 python setup.py install 2.测试 impo…

凡事想开,心情不坏~

制芰荷以为衣兮&#xff0c;集芙蓉以为裳。 不吾知其亦已兮&#xff0c;苟余情其信芳。 高余冠之岌岌兮&#xff0c;长余佩之陆离。 芳与泽其杂糅兮&#xff0c;唯昭质其犹未亏。 忽反顾以游目兮&#xff0c;将往观乎四荒。 佩缤纷其繁饰兮&#xff0c;芳菲菲其弥章。 民生各有…

Linux C高级编程——网络编程之以太网(2)

Linux网络编程——以太网 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 1、以太网帧格式 源地址和目的地址是指网卡的硬件地址&#xff08;也叫MAC地址&#xff09;&#xff0c;长度是48位&#xff0c;是在网卡出厂时固化的。用ifconfig命令查看&#…

Tomcat 打开jmx

jmx 配置后可以通过windows java客户端自带的jconsole.exe配置登陆&#xff0c;直观的查看jvm的情况及系统的各项指标&#xff1b; 一、配置linux下tomcat的jmx 具体配置如下&#xff0c;如果生产环境可以适当坐下调整。 # head /usr/local/tomcat/bin/catalina.sh #!/bin/shC…

Python学习——常见的字符串匹配

在处理字符串的时候&#xff0c;经常会有需要提取和替换的情况&#xff0c;记录自己常用的方法。 这个涉及正则表达式的用法&#xff0c;因为我是在Series的str属性在用&#xff0c;方法是同宗&#xff0c;存在略微格式的不一致。 1、替换str的中括号 Series.str.replace([\…

我不是编译器专家

这是王垠发表的一篇文章&#xff0c;转给大家看看&#xff0c;希望有些收获王垠是谁&#xff1f;王垠&#xff0c;四川大学97级本科毕业&#xff0c;保送到清华大学计算机系直博。期间曾在清华大学计算机系软件所就读&#xff0c;主要进行集成电路布线算法的研究。在此期间&…

自定义实体类简介

< DOCTYPE html PUBLIC -WCDTD XHTML TransitionalEN httpwwwworgTRxhtmlDTDxhtml-transitionaldtd> 摘要&#xff1a;有些情况下&#xff0c;非类型化的 DataSet 可能并非数据操作的最佳解决方案。本指南的目的就是探讨 DataSet 的一种替代解决方案&#xff0c;即&#…

Linux C高级编程——网络编程之TCP(3)

Linux网络编程&#xff08;三&#xff09;——TCP 宗旨&#xff1a;技术的学习是有限的&#xff0c;分享的精神是无限的。 1、TCP段格式 和UDP协议一样也有源端口号和目的端口号&#xff0c;通讯的双方由IP地址和端口号标识。32位序号、32位确认序号、窗口大小。4位首部长度和I…

机器学习——超参数调优

超参数是在开始学习过程之前设置值的参数&#xff0c;而不是通过训练得到的参数数据。超参数可以分为两种类型&#xff1a;定义模型及结构本身的参数&#xff0c;目标函数与与优化算法所需的参数&#xff0c;前者用于训练和预测阶段&#xff0c;后者用于训练阶段。 在实战过程…

单片机的Bootloader,可以实现用户轻松升级程序

去某新能源大厂出了一次差&#xff0c;这次出差是为了升级程序解决Bug&#xff0c;需要给单片机重新烧录.hex文件&#xff0c;用户已经将产品封装起来&#xff0c;无法开盖&#xff0c;只能使用CAN总线来更新程序&#xff0c;用Bootloader实现。其实就是通过上位机把.bin/hex文…

小结三种控制路由发布的方法

1、分配列表&#xff08;distribute-list&#xff09;直接过滤更新协议中的该条路由。优点是简单&#xff0c;可有效减少更新包中所带路由的数量&#xff0c;直接体现其带宽占用的优势&#xff0c;并且路由器参与的处理较少&#xff0c;节省CPU、内存等资源&#xff1b;缺点是适…