线程的同步与互斥

1. 互斥量

    在Linux 下线程使用的都是局部变量, 而我们知道, 每一个线程都独立拥有自己的一个栈, 而这些局部便令就在栈中,而线程的创建就是为了实现通信, 此时线程之间无法共享这些变量
    为了使得线程之间能够共享数据, 一次我们可以创建一个全局变量, 此时线程都在进程内部共享一个地址空间, 因此个线程之间就可以看到这个全局变量了
    但是问题又来了, 创建了全局变量, 线程之间其实看到了一份公共资源, 而此时一个线程之间由于共同访问这个局部变量很有可能造成线程之间的不安全, 为了使得线程之间能够正确访问, 我们就引入了互斥量.我们规定,当代码进入临界区执行的时候, 不允许其他线程进入该临界区. 当有多个线程要求执行临界区的代码时,此时如果临界区没有如何线程的时候, 操作系统只允许一个线程进入该临界区, 而其他的线程则必须在临界区外等待, 直到进入临界区的线程走出临界区, 并且释放互斥量.如果线程不在临界区内, 该线程不能组织其他线程进入临界区

2.互斥量相关接口
1.初始化互斥量
pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t* restict attr);参数:mutex:要初始化的互斥量attr: NULL
2.销毁互斥量
int pthread_mutex_destroy(pthread_mutex_t* mutex);
3.互斥量的加锁
int pthread_mutex_lock(pthrea_mutex_t* mutex);
4.互斥量的解锁
int pthread_mutex_unlock(pthread_mutex_t* mutex);

    注意, 互斥量处于互斥状态时, 加锁函数会将该互斥量锁定, 同时返回成功, 当发起加锁函数被调用的时候, 其他线程已经锁定互斥量, 或者当好多线程同时申请互斥量的时候, 此时线程之间就在相互竞争这个互斥量,此时, 该函数调用将会陷入阻塞状态,一直等待该互斥量, 直到拥有该互斥量的线程主动释放该互斥锁

#include<stdio.h>
#include<pthread.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>int ticket = 100;
pthread_mutex_t mutex;
void* route(void* arg)
{char* id = (char*)arg;while(1){//pthread_mutex_lock(&mutex);if(ticket > 0){usleep(10000);printf("%s sells ticket:%d\n", id, ticket);ticket--;//pthread_mutex_unlock(&mutex);}else{//pthread_mutex_unlock(&mutex);break;}}
}
int main()
{pthread_t t1, t2, t3, t4;pthread_mutex_init(&mutex, NULL);pthread_create(&t1, NULL, route, "thread 1");pthread_create(&t2, NULL, route, "thread 2");pthread_create(&t3, NULL, route, "thread 3");pthread_create(&t4, NULL, route, "thread 4");pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);return 0;
}

                                   这里写图片描述
    由上图可以看出, 由于我们未加互斥锁, 线程对数据的不正确操作造成了数据的错误,当我我们在线程进入临界区的时候临界资源加上互斥锁, 而在线程离开临界区的时候对互斥量进行释放, 此时就不会出现数据错误的现象了
                                   这里写图片描述

3. 条件变量

    当一个线程互斥访问某个变量时, 它可能发现在其他线程改变状态的之前它什么也不能做, 例如一个线程访问队列时, 它发现这个队列为空, 此时,它只能等待, 直到其他线程将结点放到该队列.这时, 为了使得各个线程之间能够同步的访问这个变量,此时就需要有一个变量, 该变量必须要符合某个条件时, 线程才能访问这个变量.

4. 相关接口
1. 初始化
int pthread_cond_init(pthread_cond_t* restrict cond, const pthread_condattr_t* restrict attr);cond: 表示要初始化的条件变量attr: NULL
2. 销毁
int pthread_cond_destroy(pthread_cond_t* cond)
3. 等待条件满足
int pthread_cond_wait(pthread_cond_t* restrict cond, pthread_mutex_t* restrict mutex);cond: 如果条件不满足, 就在条件变量上等待mutex: 互斥量, 表名线程退出时必须释放哪一个锁
4. 唤醒
int pthread_cond_broadcast(pthread_cond_t* cond);用来唤醒一群线程
int pthread_cond_signal(pthread_cond_t* cond);用来唤醒某个线程

    也许你会问, 既然线程等待, 那就直接等待就好了, 有一个条件变量就可以了, 但是为什么好要有一个互斥量呢?通过上面所述, 我们知道, 条件变量是为了使得线程之间得到同步所采用的一种手段. 当只有一个线程的时候, 条件不满足, 此时线程等待, 一直等待下去, 此时由于只有一个线程, 该线程所等待的条件将始终不会得到满足, 一次线程将会一直等待下去. 所以, 为了能够及时通知等待的线程, 必须有另外的线程对等待的这个条件(共享变量)做出预定的操作, 使得等待的线程所等待的条件得到满足, 此时, 这个条件变量是一个共享变量, 为了使得每一个线程访问该数据的正确性, 此时就必须引入互斥量来保证临界资源的正确性.

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>
#include<string.h>pthread_cond_t cond;
pthread_mutex_t mutex;void* thread1(void* arg)
{while(1){printf("%s\n", (char*)arg);pthread_cond_wait(&cond, &mutex);printf("活动\n");}
}void* thread2(void* arg)
{while(1){sleep(2);printf("%s\n", (char*)arg);pthread_cond_signal(&cond);}
}
int main()
{pthread_t t1;pthread_t t2;pthread_cond_init(&cond, NULL);pthread_mutex_init(&mutex, NULL);pthread_create(&t1, NULL, thread1, "thread1");pthread_create(&t2, NULL, thread2, "thread2");pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_cond_destroy(&cond);pthread_cond_destroy(&cond);return 0;
}

    上面代码是用来创建线程1, 线程2, 同时线程在条件变量上进行等待, 直到有线程将其唤醒, 线程2睡眠一秒, 然后将等待 cond 的线程1唤醒, 此时线程2被唤醒, 它开始执行, 执行完之后,它有开始等待条件变量 cond直到条件变量 cond 满足时再醒来.
                                   这里写图片描述
    通过上图可以看出来, 线程2要活动, 它必须等待条件变量 cond 满足时才能活动, 即线程2 必须在条件变量上等待, 直到线程 2 将其唤醒

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

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

相关文章

C语言 可变参数

http://www.cnblogs.com/zhanggaofeng/p/6434554.html //可变参数 #include <stdio.h> #include <stdlib.h> #include <string.h> //引用头文件 #include <stdarg.h>/* va_list用于声明一个变量&#xff0c;我们知道函数的可变参数列表其实就是一个字符…

c语言经典算法——查找一个整数数组中第二大数

https://www.cnblogs.com/dootoo/p/4473958.html 题目&#xff1a; 实现一个函数&#xff0c;查找一个整数数组中第二大数。 算法思想&#xff1a; 设置两个变量max1和max2&#xff0c;用来保存最大数和第二大数&#xff0c;然后将数组剩余的数依次与这两个数比较&#xff0c;如…

进程间关系和守护进程

一. 进程组/作业/会话 1.进程组 每一个进程除了有一个进程ID之外, 还属于一个进程组. 进程是一个或多个进程的集合. 通常, 它们与同一个作业向关联, 可以接收来自同一个终端下的各种命令,信号. 每一个进程组都有唯一的进程组 ID. 每一个进程组都可以有一个组长进程. 组长进程的…

猴子偷桃问题

http://blog.csdn.net/snow_5288/article/details/52561882 问题描述&#xff1a; /*有一群猴子&#xff0c;去摘了一堆桃子*/ /*商量之后决定每天吃剩余桃子的一半*/ /*当每天大家吃完桃子之后&#xff0c;有个贪心的小猴都会偷偷再吃一个桃子*/ /*按照这样的方式猴子们每天都…

网络相关基础概念

一. 相关基础概念 1.计算机网络的特点 (1)连通性:计算机网络使得上网的用户都能够彼此相连, 好像用户的计算机可以直接相连     (2)资源共享:资源共享可以是信息共享, 软件共享, 硬件共享等等. 由于网络的存在, 使得用户感觉资源就在自己身边 2. 网络 网络是由若干结点和…

linux线程同步(2)-条件变量

https://www.cnblogs.com/yuuyuu/p/5140875.html linux线程同步(2)-条件变量 一.概述 上一篇&#xff0c;介绍了互斥量。条件变量与互斥量不同&#xff0c;互斥量是防止多线程同时访问共享的互斥变量来保护临界区。条件变量…

点对点数据链路层

数据链路层的主要功能将数据转换为相应的比特流使用的信道主要有点对点的信道方式(一对一的方式), 以及广播的信道方式 一. 点对点信道的数据链路层 1. 数据链路和数据帧 链路就是从一个结点连接到相邻结点的一段物理线路(有线或者无线), 期间不准有任何的交换结点, 因此两台…

linux线程同步(1)-互斥量

http://www.cnblogs.com/yuuyuu/p/5140251.html 一.概述 互斥量是线程同步的一种机制&#xff0c;用来保护多线程的共享资源。同一时刻&#xff0c;只允许一个线程对临界区进行访问。 互斥量的工作流程&#xff1a;创建一个…

linux线程同步(3)-读写锁

http://www.cnblogs.com/yuuyuu/p/5143881.html 一.概述 读写锁与互斥量的功能类似&#xff0c;对临界区的共享资源进行保护&#xff01;互斥量一次只让一个线程进入临界区&#xff0c;读写锁比它有更高的并行性。读写锁有…

linux线程同步(5)-屏障

http://www.cnblogs.com/yuuyuu/p/5152560.html 一.概述 barrier(屏障)与互斥量&#xff0c;读写锁&#xff0c;自旋锁不同&#xff0c;它不是用来保护临界区的。相反&#xff0c;它跟条件变量一样&#xff0c;是用来协同多…

Linux多线程与同步

https://www.cnblogs.com/freedomabcd/p/7774743.html 典型的UNIX系统都支持一个进程创建多个线程(thread)。在Linux进程基础中提到&#xff0c;Linux以进程为单位组织操作&#xff0c;Linux中的线程也都基于进程。尽管实现方式有异于其它的UNIX系统&#xff0c;但Linux的多线程…

内存管理(二)

页面置换算法 当发生缺页中断的时候, 系统会在内存中选择一个页面将其换出内存, 而当换出内存的时候如果该页面的内容在内存中发生修改,则必须将该新数据重新写回到磁盘, 然后再将需要换进的数据覆盖掉原来的数据, 而当该数据在内存中没有被修改的时候, 此时就直接用需要换进的…

数据链路层:基本概念

数据链路层的定义 对数据链路层有对上的网络层接口. 对下提供物理层的接口. 定义合适的传输差错率 对传输流进行管理, 以免快速的传输的数据被淹没. 比如发送端发送信号太快, 接受方接受速度较慢, 此时数据链路层就需要提供一定的功能解决这个问题 物理层上传输的基本单元是…

数据链路层: 可靠性传输 六个协议

可靠性传输 1. 差错控制 发送方将数据帧发送, 但是当发送方发送的是一个 1的时候此时接受方却接受的是一个 0. (1)校验 接收方如果帧校验接受到的帧没有问题, 则对发送方发送一个肯定性的确认, 当对这个数据帧进行校验发现这个帧有问题的时候, 此时接受方一种是将这个数据帧…

c语言实现配置文件的读写

配置文件的格式如下&#xff1a; key1 value1 key2 value2 . . . 名值对以一个链接&#xff0c;一条记录以换行符分割 头文件&#xff1a; #include<stdio.h> #include<stdlib.h> #include <string.h> 函数原型&#xff1a; void trim(char *strIn, char *…

数据链路层: HDLC

一. 协议机 发送方和接收方. 同时有限状态机把协议形式化为一个四元组 (S,M,I,T), 其中你S表示进程和信道可能进入的集合, M 表示数据帧的状态, I 表示进程的初始状态, T 表示两两状态之间的转化. 每个系统状态可以分为发送状态, 接受状态和信道状态. 把状态用一个点进行表示,…

bob-tong 字符串函数之Strtok()函数

https://www.cnblogs.com/Bob-tong/p/6610806.html Strtok()函数详解&#xff1a; 该函数包含在"string.h"头文件中 函数原型&#xff1a; char* strtok (char* str,constchar* delimiters ); 函数功能&#xff1a;   切割字符串&#xff0c;将str切分成一个个子…

数据链路层:SLIP(串型线路IP) PPP(点对点协议)

SLIP 没有差错控制, 传输时必须知道对方IP, 传输使用于低速业务 19.2k.应用非常受限 PPP协议 1. PPP协议功能 处理错误检测 支持多协议(IP, IPX, DECnet 等) 连接时允许协商 IP 地址 允许身份验证 2. PPP 的组成 串型链路上封装数据报, 即支持异步链路也支持面向 比特…

strpbrk函数

http://blog.csdn.net/tommy_wxie/article/details/7554332 函数原型&#xff1a;extern char *strpbrk(char *str1, char *str2) 参数说明&#xff1a;str1待比较的字符串&#xff0c;str2为指定被搜索的字符串。 所在库名&#xff1a;#include <string.h> …

网络层网络层服务及其 IP 地址

ARP 协议功能 将 IP 地址通过广播(一个网段, 不能跨路由器), 目标 MAC 地址是FFFFFFFF 解析目标IP地址的 MAC 地址. IP 协议 网络层的一个协议, 是一个协议的统称, 包括 ARP(地址解析协议) 协议, ICMP(网络控制报文协议) 协议, IGMP(网际组管理协议) 协议. 其中 ICMP 和 IG…