当前位置:编程学习 > C/C++ >>

浅谈线程同步

现代操作系统都支持多线程操作了,多线程操作带来的一个麻烦就是多个线程对共享数据的访问。假设我们有线程A

和线程B,它们需要访问同一内存区域,线程A写,线程B读。一般情况下我们是希望线程A写操作完成后再进行读操

作或者线程B读操作完成后我们再进行写操作。但是在多线程中,可能由于线程A分配的时间片用完了或者其他原因导

致线程A的写操作还没完成就调用线程B来对这块共享内存进行读操作,也有可能在线程B的读操作还没完成就调用线

程A来对这块共享内存进行写操作,这些情况都有可能导致严重的逻辑错误。为了解决这一现象,我们就需要一种机

制使得各个线程能够协同工作,这就是我们所讲的线程同步机制了。

 


Windows系统中用于线程同步的常用机制有:

互斥对象(Mutex)

事件对象(Event)

信号量(Semaphore)

临界区(critical section)

可等待计时器(Waitable Timer)

 


在学习线程同步之前,我们需要先来了解下同步过程中最重要的两个概念:同步对象和等待函数。

同步对象主要有(Mutex、Event、Semaphore、critical section)。同步对象一般具有两种状态:标志的和未标志的。线

程根据是否已经完成操作将同步对象设置为标志的或未标志的。

而等待函数的功能是专门用于等待同步对象状态改变。一个线程调用等待函数后执行会暂停,直到同步对象的状态变

为标志的之后,等待函数才会返回,线程才能继续执行下去。

关于上面所讲的几种主要同步对象的概念,这篇临界区,互斥量,信号量,事件的区别讲的很详细,不懂的朋友可以

去肯看。

 


线程同步的过程:

1、在需要进行线程同步的进程中定义某种同步对象,同步对象必需是全局的,以保证需要同步的所有线程都可以访

     问到同步对象。

2、开始时,所有的线程相互独立地运行

3、当某一线程(为了方便描述设为线程A)需要访问共享资源时,若同步对象为“未标志的”,继续等待;反之,线程A将

     同步对象设为“未标志的”,并对共享资源进行访问,访问结束后再将同步对象设为“标志的”使得其它线程可以访问

     共享资源。

 


为了便于理解线程同步的过程,我们可以把我们需要访问的共享资源当成是一件放在房间里的东西,而同步对象当成

是门上的锁,而需要访问资源的线程就可以当做是取东西的人了,“标志的”状态表示门是开的,“未标志的”状态表示

门是锁着的,而此时钥匙在进去的那个人手里。当某人进入房间后,就将门锁上,其他人就无法进入了,只有等这个

人出来之后才能进入。

 


下面是一个我自己写的利用事件对象来同步访问共享内存实例:


[cpp] 
#include <windows.h>  
#include <stdio.h>  
#include <string.h>  
 
TCHAR szSharedBuffer[100] = {0};  //共享内存  
HANDLE hEvent;                    //事件对象句柄  
 
DWORD WINAPI ThreadForWrite (LPVOID lpParam); 
DWORD WINAPI ThreadForRead (LPVOID lpParam); 
 
int main() 

    HANDLE hWrite; 
    HANDLE hRead; 
 
    hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 
 
    hWrite = CreateThread(NULL, 
        0, 
        ThreadForWrite, 
        0, 
        0, 
        NULL); 
    hRead = CreateThread(NULL, 
        0, 
        ThreadForRead, 
        0, 
        0, 
        NULL); 
 
    SetEvent(hEvent); 
 
    while(1); 
    return 0; 

 
DWORD WINAPI ThreadForWrite(LPVOID lpParam) 

    while (1) 
    { 
        WaitForSingleObject(hEvent, INFINITE); 
        printf("Please input the shared chars: "); 
        scanf("%s", szSharedBuffer); 
        SetEvent(hEvent); 
    } 
    return 0; 

 
DWORD WINAPI ThreadForRead(LPVOID lpParam) 

    while (1) 
    { 
        WaitForSingleObject(hEvent, INFINITE); 
        if (!strlen(szSharedBuffer)) 
            printf("The shared chars is null now!\n"); 
        else 
            printf("The shared chars is %s\n", szSharedBuffer); 
        SetEvent(hEvent); 
    } 
    return 0; 

#include <windows.h>
#include <stdio.h>
#include <string.h>

TCHAR szSharedBuffer[100] = {0};  //共享内存
HANDLE hEvent;                    //事件对象句柄

DWORD WINAPI ThreadForWrite (LPVOID lpParam);
DWORD WINAPI ThreadForRead (LPVOID lpParam);

int main()
{
 HANDLE hWrite;
 HANDLE hRead;

 hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

 hWrite = CreateThread(NULL,
  0,
  ThreadForWrite,
  0,
  0,
  NULL);
 hRead = CreateThread(NULL,
  0,
  ThreadForRead,
  0,
  0,
  NULL);

 SetEvent(hEvent);

 while(1);
 return 0;
}

DWORD WINAPI ThreadForWrite(LPVOID lpParam)
{
 while (1)
 {
  WaitForSingleObject(hEvent, INFINITE);
  printf("Please input the shared chars: ");
  scanf("%s", szSharedBuffer);
  SetEvent(hEvent);
 }
 return 0;
}

DWORD WINAPI ThreadForRead(LPVOID lpParam)
{
 while (1)
 {
  WaitForSingleObject(hEvent, INFINITE);
  if (!strlen(szSharedBuffer))
   printf("The shared chars is null now!\n");
  else
   printf("The shared chars is %s\n", szSharedBuffer);
  SetEvent(hEvent);
 }
 return 0;
}

当然这里只是对线程同步进行一个简单的说明,真正要掌握线程同步比这里所写的要复杂的多。这将在以后的学习中

慢慢补充。

 


补充:

在上面这个例子中,创建事件对象时,第二个参数我设置的是FALSE,也就是说将事件自动重置。后来我自己改用

设置为TRUE,结果出了问题。后来想起来设置为TRUE的话,需要我们手动设置。于是在WaitForSingleObject函数

后面加了ResetEvent函数。本以为这样就可以解决问题了,但还是有问题。后来在网上问了下别人也找了点书看才知

道了原因。

 


这种做法存在两个问题,一个问题是,在单CPU平台下,同一时刻只能有一个线程在运行,假设线程ThreadForWrite

补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,