同步和互斥

当有多个线程的时候,经常需要去同步这些线程以访问同一个数据或资源。例如,假设有一个程序,其中一个线程用于把文件读到内存,而另一个线程用于统计文件中的字符数。当然,在把整个文件调入内存之前,统计它的计数是没有意义的。但是,由于每个操作都有自己的线程,操作系统会把两个线程当作是互不相干的任务分别执行,这样就可能在没有把整个文件装入内存时统计字数。为解决此问题,你必须使两个线程同步工作。

所谓同步,是指在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。如果用对资源的访问来定义的话,同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。

所谓互斥,是指散布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。如果用对资源的访问来定义的话,互斥某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

多线程同步和互斥有几种实现方法

线程间的同步方法大体可分为两类:用户模式和内核模式。顾名思义,内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。

用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。

内核模式下的方法有:事件,信号量,互斥量。

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

参见:C++多线程编程入门之经典实例

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++多线程,互斥,同步的更多相关文章

  1. 转载自~浮云比翼:Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

    Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)   介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可 ...

  2. Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

    介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间, ...

  3. Python标准库08 多线程与同步 (threading包)

    Python主要通过标准库中的threading包来实现多线程.在当今网络时代,每个服务器都会接收到大量的请求.服务器可以利用多线程的方式来处理这些请求,以提高对网络端口的读写效率.Python是一种 ...

  4. 转载:浅谈Java多线程的同步问题【很好我就留下来,多分共享】

    转载:http://www.cnblogs.com/phinecos/archive/2010/03/13/1684877.html#undefined 多线程的同步依靠的是对象锁机制,synchro ...

  5. 浅谈Java多线程的同步问题 【转】

    多线程的同步依靠的是对象锁机制,synchronized关键字的背后就是利用了封锁来实现对共享资源的互斥访问. 下面以一个简单的实例来进行对比分析.实例要完成的工作非常简单,就是创建10个线程,每个线 ...

  6. 试着用c写了一个多线程的同步

    在Java中写多线程相关的程序简单很多,在多线程中需要同步的时候,使用synchronized就行了. 最近学习c的多线程与同步,感觉实现起来,要写的代码比较多一些,这也许是因为java封装的比较好吧 ...

  7. Python 多线程、多进程 (二)之 多线程、同步、通信

    Python 多线程.多进程 (一)之 源码执行流程.GIL Python 多线程.多进程 (二)之 多线程.同步.通信 Python 多线程.多进程 (三)之 线程进程对比.多线程 一.python ...

  8. 简单的互斥同步方式——synchronized关键字详解

    目录 1. 关于synchronized关键字 2. synchronized的原理和实现细节 2.1 synchronized可以用在那些地方 2.2 synchronized是如何实现线程互斥访问 ...

  9. C#多线程的同步与通信

    C#中使用lock和Monitor控制多线程对资源的使用,最常见的生产者和消费者问题就是多线程同步和通信的经典例子.了解C#多线程的同步与通信. 一.关于lock和Monitor lock可以把一段代 ...

  10. 扯扯python的多线程的同步锁 Lock RLock Semaphore Event Condition

    我想大家都知道python的gil限制,记得刚玩python那会,知道了有pypy和Cpython这样的解释器,当时听说是很猛,也就意味肯定是突破了gil的限制,最后经过多方面测试才知道,还是那德行… ...

随机推荐

  1. npm私有仓库搭建

    背景 Node.js开发本地项目,有时不同项目之间存在依赖,如果不想把项目发布到npm社区的仓库,则需要有自己本地的仓库. 有些公司采用的是内网开发,很多npm资源无法从内网去下载. sinopia( ...

  2. 为了让开发者写MaxCompute SQL更爽,DataWorks 增强SQL 编辑器功能

    众所周知,数据开发和分析的同学每天都要花大量时间写MaxCompute SQL:Dataworks作为数据开发的IDE直接影响着大家的开发效率,这次新上线的Dataworks我们在编辑体验上做了很多工 ...

  3. [2018HN省队集训D1T3] Or

    [2018HN省队集训D1T3] Or 题意 给定 \(n\) 和 \(k\), 求长度为 \(n\) 的满足下列条件的数列的数量模 \(998244353\) 的值: 所有值在 \([1,2^k)\ ...

  4. JSTORM 问题排查

    ## 运行时topology的task列表中报"task is dead"错误有几个原因可能导致出现这个错误: 1. task心跳超时,导致nimbus主动kill这个task所在 ...

  5. [HNOI2002]营业额统计(splay基础)

    嘟嘟嘟 这几天开始搞平衡树了,\(splay\)理解起来感觉还行,然而代码看了半天才勉强看懂. 我这篇博客应该不算什么入门讲解,因为我觉得我讲不明白,所以只能算自己的学习笔记吧. 这道题就是有\(n\ ...

  6. 【转】Android 旋转动画,停止和持续旋转

    旋转180度后停止 RotateAnimation rotate; rotate =new RotateAnimation(0f,180f,Animation.RELATIVE_TO_SELF, 0. ...

  7. Hive学习之路 (二十)Hive 执行过程实例分析

    一.Hive 执行过程概述 1.概述 (1) Hive 将 HQL 转换成一组操作符(Operator),比如 GroupByOperator, JoinOperator 等 (2)操作符 Opera ...

  8. Day8 Servlet

    HttpServletRequest 说明 公共接口类HttpServletRequest继承自ServletRequest.客户端浏览器发出的请求被封装成为一个HttpServletRequest对 ...

  9. openresty + orange centos7 安装

    Orange Orange是一个基于OpenResty的API网关.除Nginx的基本功能外,它还可用于API监控.访问控制(鉴权.WAF).流量筛选.访问限速.AB测试.动态分流等.它有以下特性: ...

  10. 20145203盖泽双 《Java程序设计》第8周学习总结

    20145203盖泽双 <Java程序设计>第8周学习总结 教材学习内容总结 1.java.util.logging包提供了日志功能相关类与接口,使用日志的起点是logger类,Logge ...