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的限制,最后经过多方面测试才知道,还是那德行… ...
随机推荐
- Paxos可容错的一致性协议
一致性问题要求多个process对一个值达成一致.基于消息传递的分布式系统中,在不考虑消息篡改等拜占庭错误的情况下,Paxos可以解决在进程退出,消息延迟,丢失,重复等异常发生的环境中对某个值达成一致 ...
- Oracle使用order by排序关于null值处理
select * from dual order by age desc nulls last select * from test order by age asc nulls first sqls ...
- iOS设计模式 - 中介者
iOS设计模式 - 中介者 原理图 说明 用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互. 注:中介者对象本身没有复用价值 ...
- 一键安装lnmp1.5
系统需求: CentOS/RHEL/Fedora/Debian/Ubuntu/Raspbian/Deepin/Aliyun/Amazon/Mint Linux发行版 需要5GB以上硬盘剩余空间,MyS ...
- The expression of type Integer is unboxed into int
问题:The expression of type Integer is unboxed into int 原因:java的包装类,方法里面要的是Integer,传入的参数确实int类型 解决方案: ...
- November 14th 2016 Week 47th Monday
There are far, far better things ahead than any we leave behind. 前方,有更美好的未来. Can I see those better ...
- DOM操作案例之--全选与反选
全选与反选在表单类的项目中还是很常见的,电商项目中的购物车一定少不了这个功能. 下面我只就用一个简单的案例做个演示吧. <div class="wrap"> <t ...
- jquery与json的结合
通过AJAX异步减少网络内容传输,而JSON则可以把传输内容缩减到纯数据:然后利用jQuery内置的AJAX功能直接获得JSON格式的数据:在客户端直接绑定到数据控件里面,从而达到最优. 1 2 3 ...
- 洛谷 P2764 最小路径覆盖问题【最大流+拆点+路径输出】
题目链接:https://www.luogu.org/problemnew/show/P2764 题目描述 «问题描述: 给定有向图G=(V,E).设P 是G 的一个简单路(顶点不相交)的集合.如果V ...
- shuffle() 函数
shuffle() 方法将序列的所有元素随机排序. 以下是 shuffle()方法的语法: shuffle (lst ) 注意:此函数是无法直接访问,需要导入 random 模块,然后通过 rando ...