多线程编程--5种方法实现线程同步

1:用Interlocked系列函数实现线程同步;

2:用CRITICAL_SECTION及其系列函数实现线程同步;

3:用RTL_SRWLOCK及其系列函数实现线程同步;

4:用事件内核对象实现线程同步;

5:用信号量内核对象实现线程同步;

 

1:用Interlocked系列函数实现线程同步实例如下:

//旋转锁
#include <iostream>
using namespace std;
#include <process.h>
#include <windows.h>
const int threadNum=10;
HANDLE hThread[threadNum];
volatile unsigned int ISOK=0;
unsigned int _stdcall Interlocked(PVOID threadId)
{
    while(InterlockedExchange(&ISOK,1)==1) ;
    cout<<"线程:"<<*(int*)threadId<<"开始"<<endl;
    Sleep(100);
    cout<<"线程:"<<*(int*)threadId<<"结束"<<endl;
    InterlockedExchange(&ISOK,0);
    return 0;
}

void InterlockedTest()
{
    int threadId[threadNum];
    for(int i=0;i<threadNum;i++)
    {
        threadId[i]=i+1;
    }
    cout<<"1:用Interlocked系列函数实现线程同步"<<endl;
    for(int i=0;i<10;i++){
        hThread[i]=(HANDLE)_beginthreadex(NULL, 0, Interlocked,threadId+i, 0, NULL);
    }
    WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE);
    for(int i=0;i<threadNum;i++)
    {
        CloseHandle(hThread[i]);
    }
}

InterlockedExchange确保以原子的方式操作数据。执行速度非常快,缺点是如果要同步的部分执行的时间比较长的话,while循环会一直轮询操作,浪费CPU的时间,在单核CPU的系统中,可能会出现while一直暂用CPU导致其他线程不能修改ISOK的值,导致不能跳出while循环,出现死循环。还有就是线程的优先级问题也能导致问题。

2:用CRITICAL_SECTION及其系列函数实现线程同步实例如下:

//关键段
#include <iostream>
using namespace std;
#include <process.h>
#include <windows.h>
const int threadNum=10;
HANDLE hThread[threadNum];
CRITICAL_SECTION g_cs;//构造一个CRITICAL_SECTION实例
unsigned int _stdcall  CriticalSection(PVOID threadId)
{
    EnterCriticalSection(&g_cs);//进入关键段
    cout<<"线程:"<<*(int*)threadId<<"开始"<<endl;
    Sleep(100);
    cout<<"线程:"<<*(int*)threadId<<"结束"<<endl;
    LeaveCriticalSection(&g_cs);//进入关键段
    return 0;
}

void CriticalSectionTest()
{
    int threadId[threadNum];
    for(int i=0;i<threadNum;i++)
    {
        threadId[i]=i+1;
    }
    InitializeCriticalSection(&g_cs);//初始化g_cs的成员
    cout<<"2:用CRITICAL_SECTION及其系列函数实现线程同步"<<endl;
    for(int i=0;i<10;i++){
        hThread[i]=(HANDLE)_beginthreadex(NULL, 0, CriticalSection,threadId+i, 0, NULL);
    }
    WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE);
    for(int i=0;i<threadNum;i++)
    {
        CloseHandle(hThread[i]);
    }
    DeleteCriticalSection(&g_cs);//删除关键段
}

CRITICAL_SECTION同样是以原子的方式操作数据,也只有以原子的方式操作数据才能实现线程的同步,所有实现线程同步的方法,最核心的部分就是以原子的方式操作数据,CRITICAL_SECTION执行的速度非常快,其内部有一个事件内核对象,当出现资源争夺的时候,才会出现初始化这个事件内核对象,由于CRITICAL_SECTION执行非常快可能不会出现资源争夺,也就没有必要创建这个事件内核对象,这个事件内核对象创建后,会将当前线程之外的线程挂起,并记录这些线程需要这个资源,其他线程就不会浪费CPU的时间,而这些被挂起的线程将由用户模式变成内核模式,当这些线程需要的资源可用时,系统会将其中一个线程唤醒。

还有一点值得注意:如果要同步的代码执行得很快,在出现争夺资源的时候,系统把其他线程挂起,而当前线程又马上执行完成了,系统又将挂起的线程唤醒,这个过程是非常浪费CPU的,也影响程序的性能,为了避免这种情况,可以结合旋转锁和CRITICAL_SECTION,先用旋转锁轮询一定次数,还不能获得资源,再将线程挂起,等待资源被释放,系统再将线程唤醒,实现这一功能的就是方法

InitializeCriticalSectionAndSpinCount(

LPCRITICAL_SECTION lpCriticalSection,

DWORD dwSpinCount//旋转锁轮询的次数

);

除了初始化CRITICAL_SECTION用的是方法InitializeCriticalSectionAndSpinCount,而不是方法InitializeCriticalSection,其他的都是一样的。

3:用RTL_SRWLOCK及其系列函数实现线程同步实例如下:

//读写锁
#include <iostream>
using namespace std;
#include <process.h>
#include <windows.h>
const int threadNum=10;
HANDLE hThread[threadNum];
RTL_SRWLOCK  lock;//构造一个CRITICAL_SECTION实例
unsigned int _stdcall  SrwLock(PVOID threadId)
{
    AcquireSRWLockExclusive(&lock);//进入读写锁
    cout<<"线程:"<<*(int*)threadId<<"开始"<<endl;
    Sleep(100);
    cout<<"线程:"<<*(int*)threadId<<"结束"<<endl;
    ReleaseSRWLockExclusive(&lock);//进入读写锁
    return 0;
}

void SrwLockTest()
{
    int threadId[threadNum];
    for(int i=0;i<threadNum;i++)
    {
        threadId[i]=i+1;
    }
    InitializeSRWLock(&lock);//初始化lock的成员
    cout<<"3:用RTL_SRWLOCK及其系列函数实现线程同步"<<endl;
    for(int i=0;i<10;i++){
        hThread[i]=(HANDLE)_beginthreadex(NULL, 0, SrwLock,threadId+i, 0, NULL);
    }
    WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE);
    for(int i=0;i<threadNum;i++)
    {
        CloseHandle(hThread[i]);
    } 

}

SRWLock的目的和关键段是一样的,就是对资源的保护,不让其他线程访问。不同的是,它区分线程是读线程还是写线程。我们都是知道,一个资源可以同时被多个线程同时读,就是不能同时读,或是读写。也是是说写必须是独占的方式,而读可以以共享的方式访问,如果以共享的方式访问肯定就比CRITICAL_SECTION性能好。

4:用事件内核对象实现线程同步实例如下:

//事件
#include <iostream>
using namespace std;
#include <process.h>
#include <windows.h>
const int threadNum=10;
HANDLE hThread[threadNum];
HANDLE event1; 

unsigned int _stdcall  Event(PVOID threadId)
{
    WaitForSingleObject(event1,INFINITE);
    int* p=(int*)threadId;
    cout<<"线程:"<<*p<<"开始"<<endl;
    Sleep(100);
    cout<<"线程:"<<*p<<"结束"<<endl;
    SetEvent(event1);
    return 1;
}

void EventTest()
{
    int threadId[threadNum];
    for(int i=0;i<threadNum;i++)
    {
        threadId[i]=i+1;
    }
    event1=CreateEvent(NULL,false,true,NULL);
    cout<<"4:用事件内核对象实现线程同步"<<endl;
    for(int i=0;i<threadNum;i++)
    {
        hThread[i] =(HANDLE)_beginthreadex(NULL, 0, Event ,threadId+i, 0, NULL);
    }
    WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE);
    for(int i=0;i<threadNum;i++)
    {
        CloseHandle(hThread[i]);
    }
    CloseHandle(event1);
}

用内核对象实现线程同步,一个函数是必须知道的,它就是WaitForSingleObject。

DWORD WaitForSingleObject(

HANDLE hHandle,//内核对象的句柄

DWORD dwMilliseconds//等待时间

);

该函数会一直等待,直到被指定的内核对象被触发为止,或是等待的时间结束返回。

CreateEvent(

LPSECURITY_ATTRIBUTES lpEventAttributes,//安全控制

BOOL bManualReset,//true:手动重置事件,false:自动重置事件

BOOL bInitialState,//true:有信号,false:无信号

LPCWSTR lpName//事件名称

);

bManualReset为true表示事件触发了并一直处于触发状态,就像打开的门,打开之后就是一直开着,没有自动关上;false:一打开放一个进去进关了,需要用SetEvent再次触发事件。

5:用信号量内核对象实现线程同步实例如下:

//信号量
#include <iostream>
using namespace std;
#include <process.h>
#include <windows.h>
const int threadNum=10;
HANDLE hThread[threadNum];
HANDLE semaphore;
unsigned int _stdcall  Semaphore(PVOID threadId)
{
    WaitForSingleObject(semaphore, INFINITE);
    cout<<"线程:"<<*(int*)threadId<<"开始"<<endl;
    Sleep(100);
    cout<<"线程:"<<*(int*)threadId<<"结束"<<endl;
    ReleaseSemaphore(semaphore,1,NULL);
    return 0;
}

void SemaphoreTest()
{
    int threadId[threadNum];
    for(int i=0;i<threadNum;i++)
    {
        threadId[i]=i+1;
    }
    semaphore=CreateSemaphore(NULL,1,1,NULL);
    cout<<"5:用信号量内核对象实现线程同步"<<endl;
    for(int i=0;i<10;i++){
        hThread[i]=(HANDLE)_beginthreadex(NULL, 0, Semaphore,threadId+i, 0, NULL);
    }
    WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE);
    for(int i=0;i<threadNum;i++)
    {
        CloseHandle(hThread[i]);
    }
    CloseHandle(semaphore);
}

信号量内核对象用来对资源进行计数。创建信号量内核对象的方法如下:

CreateSemaphore(

LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,//安全控制

LONG lInitialCount,//初始资源数量

LONG lMaximumCount,//最大并发数量

LPCWSTR lpName//号量的名称

);

lMaximumCount表示最大并发数量,可以用来设置系统的最大并发数量,如果我们把他的值设为1,lInitialCount也设为1,就是只有一个资源,且每次只能一个线程访问,这样就可以实现线程同步。

在实现线程同步时,建议用方法2和方法3,如不能解决你的需求,再用方法4,方法5,用内核对象实现的线程同步性能要差一些。

多线程编程5中实现线程同步的方法介绍差多就到此了,大家可能还有一些疑问,可以看看我之前关于多线程基础知识的一些介绍:

Windows线程基础

Windows内核对象简介

Windows几种线程同步方法介绍

时间: 2024-10-13 18:30:40

多线程编程--5种方法实现线程同步的相关文章

C#多线程编程系列(三)- 线程同步

目录 1.1 简介 1.2 执行基本原子操作 1.3 使用Mutex类 1.4 使用SemaphoreSlim类 1.5 使用AutoResetEvent类 1.6 使用ManualResetEventSlim类 1.7 使用CountDownEvent类 1.8 使用Barrier类 1.9 使用ReaderWriterLockSlim类 1.10 使用SpinWait类 参考书籍 笔者水平有限,如果错误欢迎各位批评指正! 1.1 简介 本章介绍在C#中实现线程同步的几种方法.因为多个线程同时

APUE学习之多线程编程(二):线程同步

为了保证临界资源的安全性和可靠性,线程不得不使用锁,同一时间只允许一个或几个线程访问变量.常用的锁有互斥量,读写锁,条件变量 一.互斥量 互斥量是用pthread_mutex_t数据类型表示的,在使用之前,必须对其进行初始化,可以把它设置为PTHREAD_MUTEX_INITIALIZER(只适于静态分配的互斥量),也可以通过调用pthread_mutex_init函数进行初始化,最后还要调用pthread_mutex_destroy进行释放. #include <pthread.h> int

C#编程总结(三)线程同步

C#编程总结(三)线程同步 在应用程序中使用多个线程的一个好处是每个线程都可以异步执行.对于 Windows 应用程序,耗时的任务可以在后台执行,而使应用程序窗口和控件保持响应.对于服务器应用程序,多线程处理提供了用不同线程处理每个传入请求的能力.否则,在完全满足前一个请求之前,将无法处理每个新请求.然而,线程的异步特性意味着必须协调对资源(如文件句柄.网络连接和内存)的访问.否则,两个或更多的线程可能在同一时间访问相同的资源,而每个线程都不知道其他线程的操作. "如果觉得有用,请帮顶! 如果有

C#并行编程(6):线程同步面面观

理解线程同步 线程的数据访问 在并行(多线程)环境中,不可避免地会存在多个线程同时访问某个数据的情况.多个线程对共享数据的访问有下面3种情形: 多个线程同时读取数据: 单个线程更新数据,此时其他线程读取数据: 多个线程同时更新数据. 显而易见,多个线程同时读取数据是不会产生任何问题的.仅有一个线程更新数据的时候,貌似也没有问题,但真的没有问题吗?多个线程同时更新数据,很明显,你可能把我的更改覆盖掉了,数据从此不再可信. 什么是线程同步 为了解决多线程同时访问共享数据可能导致数据被破坏的问题,我们

实现多线程的两种方法:继承Thread类或实现Runnable接口

实现多线程的两种方法:继承Thread类或实现Runnable接口 Java中实现多线程有两种方法:继承Thread类和实现Runnable接口,在程序开发中只要是多线程,我们一般都是实现Runnable接口,原因归结为一点:实现接口比继承类要好. 多线程的第一种实现方式:继承Thread类 步骤如下 创建一个继承Thread的类(假定为A),并重写Thread的run方法 构造一个A类对象,假定为aa 调用aa的start方法.(start方法是从Thread继承过来的) 具体例子如下 pac

Linux中四种进程或线程同步互斥控制方法

原文地址:http://blog.itpub.net/10697500/viewspace-612045/ 一.Linux中 四种进程或线程同步互斥的控制方法: 1.临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问. 2.互斥量:为协调共同对一个共享资源的单独访问而设计的. 3.信号量:为控制一个具有有限数量用户资源而设计. 4.事 件:用来通知线程有一些事件已发生,从而启动后继任务的开始. 二.临界区(Critical Section) 保证在某一时刻只有一个线程

【C/C++多线程编程之十】pthread线程私有数据

多线程编程之线程私有数据 Pthread是 POSIX threads 的简称,是POSIX的线程标准.  线程同步从互斥量[C/C++多线程编程之六]pthread互斥量,信号量[C/C++多线程编程之七]pthread信号量,条件变量[C/C++多线程编程之八]pthread条件变量,读写锁[C/C++多线程编程之九]pthread读写锁,多线程的同步机制已经有了清晰深入的探究,多线程编程的精髓所在,需要深入理解.        线程私有数据TSD(Thread-specific Data)

C++ socket编程——3种方法发送不同类型的数据

socket传送数据,一般来讲是char型的,如何传送我们需要的数据类型勒? 1.结构体:2, Json序列化,3. 定义一个class. 1.结构体 相对来说简单点,看看网上的一个例子: 假设需要传送的结构体如下: struct person{ char name[20]; int age; float high; }; 可在发送数据的地方对数据进行处理,将其转换成一个字符串进行传送,而在接受方定义相同的结构体对这个字符串进行解析即可. 发送方代码如下: char temp[100];   

Python并发编程之创建多线程的几种方法(二)

大家好,并发编程 进入第二篇. 今天的内容会比较基础,主要是为了让新手也能无障碍地阅读,所以还是要再巩固下基础.学完了基础,你们也就能很顺畅地跟着我的思路理解以后的文章. 本文目录 学会使用函数创建多线程 学会使用类创建多线程 多线程:必学函数讲解 经过总结,Python创建多线程主要有如下两种方法: 函数 类 接下来,我们就来揭开多线程的神秘面纱. . 学会使用函数创建多线程 在Python3中,Python提供了一个内置模块 threading.Thread,可以很方便地让我们创建多线程.