C++多线程,互斥,同步
同步和互斥
当有多个线程的时候,经常需要去同步这些线程以访问同一个数据或资源。例如,假设有一个程序,其中一个线程用于把文件读到内存,而另一个线程用于统计文件中的字符数。当然,在把整个文件调入内存之前,统计它的计数是没有意义的。但是,由于每个操作都有自己的线程,操作系统会把两个线程当作是互不相干的任务分别执行,这样就可能在没有把整个文件装入内存时统计字数。为解决此问题,你必须使两个线程同步工作。
所谓同步,是指在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。如果用对资源的访问来定义的话,同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
所谓互斥,是指散布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。如果用对资源的访问来定义的话,互斥某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
多线程同步和互斥有几种实现方法
线程间的同步方法大体可分为两类:用户模式和内核模式。顾名思义,内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。
用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。
内核模式下的方法有:事件,信号量,互斥量。
1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
2、互斥量:为协调共同对一个共享资源的单独访问而设计的。
3、信号量:为控制一个具有有限数量用户资源而设计。
4、事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始。
进程间通信方式
(1)管道(pipe)及有名管道(named pipe):管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。
(2)信号(signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致的。
(3)消息队列(message queue):消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息。
(4)共享内存(shared memory):可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。
(5)信号量(semaphore):主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段。
(6)套接字(socket):这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。
C++多线程几种实现
windows下通过CreateThread
头文件 <windows.h>
创建线程API:
HANDLE CreateThread(
__in SEC_ATTRS
SecurityAttributes, //线程安全属性
__in ULONG
StackSize, // 堆栈大小
__in SEC_THREAD_START
StartFunction, // 线程函数
__in PVOID
ThreadParameter, // 线程参数
__in ULONG
CreationFlags, // 线程创建属性
__out PULONG
ThreadId // 线程ID
);
使用步骤:
HANDLE hThread = CreateThread(NULL, , Fun, NULL, , NULL);
CloseHandle(hThread);
互斥API:互斥量实现
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner, // 指定该资源初始是否归属创建它的进程
LPCTSTR lpName // 指定资源的名称
); BOOL ReleaseMutex(
HANDLE hMutex // 该函数用于释放一个独占资源,进程一旦释放该资源,该资源就不再属于它了
); DWORD WaitForSingleObject(
HANDLE hHandle, // 指定所申请的资源的句柄
DWORD dwMilliseconds // 一般指定为INFINITE,表示如果没有申请到资源就一直等待该资源
);
互斥API:临界区实现
CRITICAL_SECTION cs;
InitializeCriticalSection(&cs);
EnterCriticalSection(&cs);
LeaveCriticalSection(&cs);
DeleteCriticalSection(&cs);
例子:
#include <iostream>
#include <windows.h>
using namespace std; HANDLE hMutex; DWORD WINAPI Fun(LPVOID lpParamter)
{
while () {
WaitForSingleObject(hMutex, INFINITE);
cout << "Fun display!" << endl;
Sleep();
ReleaseMutex(hMutex);
}
} int main()
{
HANDLE hThread = CreateThread(NULL, , Fun, NULL, , NULL);
hMutex = CreateMutex(NULL, FALSE, "screen");
CloseHandle(hThread);
while () {
WaitForSingleObject(hMutex, INFINITE);
cout << "main display!" << endl;
Sleep();
ReleaseMutex(hMutex);
} return ;
}
linux下则pthread库(POSIX)
头文件<phread.h>
创建线程API:
int pthread_create(pthread_t *tidp, //第一个参数为指向线程标识符的指针。
const pthread_attr_t *attr, //第二个参数用来设置线程属性。
(void*)(*start_rtn)(void*), //第三个参数是线程运行函数的起始地址。
void *arg //最后一个参数是运行函数的参数
); //线程通过调用pthread_exit函数终止执行
void pthread_exit(void* retval); //函数pthread_join用来等待一个线程的结束,线程间同步的操作,阻塞函数
int pthread_join(pthread_t thread, void **retval);
例子:
#include <stdio.h>
#include <pthread.h>
#include <sched.h> static int run = ;
static int retvalue; void *start_routine(void *arg)
{
int *running = arg;
printf("子线程初始化完毕,传入参数为:%d\n", *running);
while (*running)
{
printf("子线程正在运行\n");
usleep();
}
printf("子线程退出\n"); retvalue = ;
pthread_exit((void*)&retvalue);
} int main(void)
{
pthread_t pt;
int ret = -;
int times = ;
int i = ;
int *ret_join = NULL; ret = pthread_create(&pt, NULL, (void*)start_routine, &run);
if (ret != )
{
printf("建立线程失败\n");
return ;
}
usleep();
for (; i < times; i++)
{
printf("主线程打印\n");
usleep();
}
run = ;
pthread_join(pt, (void*)&ret_join);
printf("线程返回值为:%d\n", *ret_join);
return ; }
互斥步骤:
互斥锁:等价于二元信号量
- pthread_mutex_init初始化一个锁
- pthread_mutex_destory销毁互斥锁
- pthread_mutex_lock原子方式给加锁,如果自身线程对自身加锁的锁再次加锁,则死锁;其余线程加锁则不会死锁,但会阻塞。
- pthread_mutex_trylock非阻塞加锁
- pthread_mutex_unlock原子操作方式来解锁
注意:锁分为递归锁和非递归锁,递归锁可以对自身加锁的锁加锁多次。
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
生产者消费者例子:(仅仅互斥,没有用信号量同步)
/*
* ex04-mutex.c
* 线程实例
*/
#include <stdio.h>
#include <pthread.h>
#include <sched.h> void *producter_f (void *arg);
void *consumer_f (void *arg); int buffer_has_item=;
pthread_mutex_t mutex; int running = ; int main (void)
{
pthread_t consumer_t;
pthread_t producter_t; pthread_mutex_init (&mutex,NULL); pthread_create(&producter_t, NULL,(void*)producter_f, NULL );
pthread_create(&consumer_t, NULL, (void *)consumer_f, NULL);
usleep();
running =;
pthread_join(consumer_t,NULL);
pthread_join(producter_t,NULL);
pthread_mutex_destroy(&mutex); return ;
} void *producter_f (void *arg)
{
while(running)
{
pthread_mutex_lock (&mutex);
buffer_has_item++;
printf("生产,总数量:%d\n",buffer_has_item);
pthread_mutex_unlock(&mutex);
}
} void *consumer_f(void *arg)
{
while(running)
{
pthread_mutex_lock(&mutex);
buffer_has_item--;
printf("消费,总数量:%d\n",buffer_has_item);
pthread_mutex_unlock(&mutex);
}
}
同步步骤:信号量 头文件<semaphore.h>
- sem_init函数用于初始化一个未命名的信号量
- sem_destory函数用于销毁信号量
- sem_wait函数以原子操作的方式将信号量的值减1,如果信号量的值为0,则阻塞。
- sem_trywait上面的非阻塞版本,如果信号量为0,立即返回-1,并设置errno为EAGIN
- sem_post将信号量的值加1
参见《Linux网络编程》
C11std::thread
参见:C11多线程
三方库:如boost的thread
C++多线程,互斥,同步的更多相关文章
- 转载自~浮云比翼:Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)
Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥) 介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可 ...
- Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)
介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间, ...
- Python标准库08 多线程与同步 (threading包)
Python主要通过标准库中的threading包来实现多线程.在当今网络时代,每个服务器都会接收到大量的请求.服务器可以利用多线程的方式来处理这些请求,以提高对网络端口的读写效率.Python是一种 ...
- 转载:浅谈Java多线程的同步问题【很好我就留下来,多分共享】
转载:http://www.cnblogs.com/phinecos/archive/2010/03/13/1684877.html#undefined 多线程的同步依靠的是对象锁机制,synchro ...
- 浅谈Java多线程的同步问题 【转】
多线程的同步依靠的是对象锁机制,synchronized关键字的背后就是利用了封锁来实现对共享资源的互斥访问. 下面以一个简单的实例来进行对比分析.实例要完成的工作非常简单,就是创建10个线程,每个线 ...
- 试着用c写了一个多线程的同步
在Java中写多线程相关的程序简单很多,在多线程中需要同步的时候,使用synchronized就行了. 最近学习c的多线程与同步,感觉实现起来,要写的代码比较多一些,这也许是因为java封装的比较好吧 ...
- Python 多线程、多进程 (二)之 多线程、同步、通信
Python 多线程.多进程 (一)之 源码执行流程.GIL Python 多线程.多进程 (二)之 多线程.同步.通信 Python 多线程.多进程 (三)之 线程进程对比.多线程 一.python ...
- 简单的互斥同步方式——synchronized关键字详解
目录 1. 关于synchronized关键字 2. synchronized的原理和实现细节 2.1 synchronized可以用在那些地方 2.2 synchronized是如何实现线程互斥访问 ...
- C#多线程的同步与通信
C#中使用lock和Monitor控制多线程对资源的使用,最常见的生产者和消费者问题就是多线程同步和通信的经典例子.了解C#多线程的同步与通信. 一.关于lock和Monitor lock可以把一段代 ...
- 扯扯python的多线程的同步锁 Lock RLock Semaphore Event Condition
我想大家都知道python的gil限制,记得刚玩python那会,知道了有pypy和Cpython这样的解释器,当时听说是很猛,也就意味肯定是突破了gil的限制,最后经过多方面测试才知道,还是那德行… ...
随机推荐
- Vue2学习笔记:组件(Component)
组件 组件(Component)是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.在较高层面上,组件是自定义元素, Vue.js 的编译器为它添加特殊功能.在有些情况 ...
- 向服务器post或者get数据返回
#region 向服务器端Get值返回 /// <summary> /// 向服务器端Get返回 /// </summary> ///<see cref="Au ...
- [UI] 精美UI界面欣赏[12]
精美UI界面欣赏[12]
- (1)List集合 (2)Queue集合 (3)Set集合
1.List集合(重中之重)1.1 基本概念 java.util.List接口是Collection接口的子接口,该接口中元素有先后放入次序并允许重复 该接口的主要实现类:ArrayList类.Lin ...
- 微信小程序初始化 operateWXData:fail invalid scope
初学者开发微信小程序,可以使用云开发来进行微信小程序的开发. 第一次使用开发工具遇到的问题 解决方案:1.找到云开发 2.点击开通,选择合适自己的开发环境: 3.完成后,返回开发工具界面点击项目第一个 ...
- 开源作业调度框架 - Quartz.NET - 实战使用1
简介: 第一步:下载Quartz.NET 下载Quartz.NET只需要打开网址选择适宜的版本进行下载解压缩即可. 目前最新版本是2.3.3,压缩包为6MB,不过鉴于国内网速.我还是加一下博客园的下载 ...
- php解析xml文件的方法
最近一段时间在做模板包导入.模板包中包含有xml文件,,需要解析成给定的php数组格式. 我接触到了两种方法,分别是DOMDocument 方法和 simple_load_file. 个人偏好后一种, ...
- SOJ1022 Uniform Generator
Computer simulations often require random numbers. One way to generate pseudo-random numbers is via ...
- python第十二课——for in循环
1.for...in循环: 有两个使用场景: 场景一:for in和range对象配合使用 range对象的引入讲解 格式:range([start,end,step]): 特点:索引满足含头不含尾的 ...
- swift的enum模式—对Alamofire入口的解析--数据结构与操作结合的模式
swift的枚举模式是数据结构与操作结合的模式 1.enum本质是一个类型,可以定义变量: 它定义的变量可以用到其它变量用的的任何地方:函数的输入.输出.成员变量.临时变量等: 这个变量还可以带有附加 ...