多线程之同步问题小结

线程同步
多个线程需要访问同一个资源时,你应该考虑同步问题;例如:银行系统中,可能使用多个线程对数据

库进行操作,如:存储、更新、查询线程同时访问数据库,极可能出现:查询到的数据是未完全更新的

数据。还有就是火车售票系统,多窗口售票可以认为是多个线程,多窗口共享总票资源(tickets),此时

如果不对共享资源进行同步处理,就会出现:卖第0张票的情况!
long tickets = 100;  //一共100张票

unsigned _stdcall ThreadProc1(void * pArgu = NULL)
{
 while(TRUE)
 {
  if(tickets>0)
  {
   FILE * fp = fopen("c://ticketseller.txt", "a");
   fprintf(fp, "A窗口出票:%d/n", tickets--);
   fclose(fp);
  }
  else
  {
   FILE * fp = fopen("c://ticketseller.txt", "a");
   fprintf(fp, "A窗口票卖完了:%d/n", tickets);
   fclose(fp);
   break;
  }
 }
 return 0;
}


unsigned _stdcall ThreadProc2(void * pArgu = NULL)
{
 while(TRUE)
 {
  if(tickets>0)
  { 
   Sleep(1); //B售票员打个顿,停了1毫秒(0.001秒)
   FILE * fp = fopen("c://ticketseller.txt", "a");
   fprintf(fp, "B窗口出票:%d/n", tickets--);
   fclose(fp);
  }
  else
  {
   FILE * fp = fopen("c://ticketseller.txt", "a");
   fprintf(fp, "B窗口票卖完了:%d/n", tickets);
   fclose(fp);
   break;
  }
 }
 return 0;
}

 

#include "process.h"
void CMy1006Dlg::OnOK()
{
 unsigned nThreadID = 0;
 FILE * fp = fopen("c://ticketseller.txt", "w");
 fprintf(fp, "开始测试多线程卖票:%d/n", tickets);
 fclose(fp);

 _beginthreadex(NULL,
  0,
  ThreadProc1,
  NULL,
  0,
  &nThreadID);
 _beginthreadex(NULL,
  0,
  ThreadProc2,
  NULL,
  0,
  &nThreadID);
}

//上述代码,你可能会看到下面你不愿意看到的结果:
开始测试多线程卖票:100
A窗口出票:100
A窗口出票:99
B窗口出票:98
A窗口出票:97
A窗口出票:96
A窗口出票:95
A窗口出票:94
A窗口出票:93
B窗口出票:92
A窗口出票:91
A窗口出票:90
A窗口出票:89
A窗口出票:88
A窗口出票:87
B窗口出票:86
A窗口出票:85
A窗口出票:84
B窗口出票:83
A窗口出票:82
A窗口出票:81
A窗口出票:80
A窗口出票:79
A窗口出票:78
A窗口出票:77
B窗口出票:76
A窗口出票:75
A窗口出票:74
A窗口出票:73
A窗口出票:72
A窗口出票:71
B窗口出票:70
A窗口出票:69
A窗口出票:68
A窗口出票:67
A窗口出票:66
A窗口出票:65
A窗口出票:64
B窗口出票:63
A窗口出票:62
A窗口出票:61
A窗口出票:60
B窗口出票:59
A窗口出票:58
A窗口出票:57
A窗口出票:56
A窗口出票:55
A窗口出票:54
A窗口出票:53
B窗口出票:52
A窗口出票:51
A窗口出票:50
A窗口出票:49
A窗口出票:48
A窗口出票:47
B窗口出票:46
A窗口出票:45
A窗口出票:44
A窗口出票:43
A窗口出票:42
A窗口出票:41
B窗口出票:40
A窗口出票:39
A窗口出票:38
B窗口出票:37
A窗口出票:35
A窗口出票:34
A窗口出票:33
A窗口出票:32
A窗口出票:31
B窗口出票:30
A窗口出票:29
A窗口出票:28
A窗口出票:27
A窗口出票:26
A窗口出票:25
B窗口出票:24
A窗口出票:23
A窗口出票:22
A窗口出票:21
A窗口出票:20
A窗口出票:19
A窗口出票:18
B窗口出票:17
A窗口出票:16
A窗口出票:15
A窗口出票:14
A窗口出票:13
B窗口出票:12
A窗口出票:11
A窗口出票:10
A窗口出票:9
A窗口出票:8
A窗口出票:7
A窗口出票:5
A窗口出票:4
A窗口出票:3
A窗口出票:2
A窗口出票:1
A窗口票卖完了:0
B窗口出票:0
B窗口票卖完了:-1

 

解决办法很多,在Windows平台下MFC为我们封装了同步管理类:CSyncObject,我们经常看到的CEvent(

事件)、CCriticalSection(临界区)、CMutex(互斥体)、CSemaphore(信号量)都是它的派生类; 使用它们

解决同步问题就方便多了!它们的头文件都是:#include "afxmt.h"

 

一、事件(CEvent)
常用于不同线程之间在满足特定条件情况下的唤醒操作;它有两种状态,有信号和无信号状态;例如:

在一些串口通信程序中,一个线程负责监听串口状态,一个线程负责数据的界面回显;那么就可以在侦

听到串口状态发生变化之后,用信号的形式通知界面回显线程;

我经常这么干:
CEvent hUIEvent = CreateEvent(NULL, FALSE, TRUE, NULL );
//创建一个使用默认安全属性、初始无信号、手动状态、无名称的事件对象(界面线程触发事件)
侦听到串口状态发生变化时:
//...数据处理
SetEvent(hUIEvent);  //通知界面回显线程,设置事件为有信号状态;ResetEvent(hUIEvent); 设置为

无信号状态

//界面回显线程
unsigned _stdcall UIThreadProc(void * pArgus =  NULL)
{
DWORD dwWait = WaitForSingleObject(hUIEvent, 3000); //3秒检测一次事件状态
switch(dwWait)
{
 case WAIT_OBJECT_0:
 //界面数据显示
 break;
 case WAIT_TIMEOUT:
 break;
 case WAIT_FAILED:
 break;
 default:
 break;
}
return 0;
}

 

二、临界区(CCriticalSection,其构造函数没有参数)
    当多个线程访问一个独占性共享资源时,可以使用“临界区”对象。任一时刻只有一个线程可以拥有

临界区对象,拥有临界区的线程可以访问被保护起来的资源或代码段,其他希望进入临界区的线程将全

部被挂起等待,直到拥有临界区的线程放弃临界区时为止,这样就保证了不会在同一时刻出现多个线程

访问共享资源。

CCriticalSection criticalSection;
criticalSection.Lock();
//...被保护的代码段
criticalSetion.Unlock();

注意:上面提到的火车售票问题,由于tickets不是独占性资源,就不要使用临界区对象;一旦一个售票

窗口得到临界资源,他不卖票反而吃饭去了,其他窗口则有票不能卖!一定要注意了,是独占性资源可以

使用临界区对象,这个资源我使用了,别人都别想用;

 

三、互斥体(CMutex)
和临界区类似,不同的是:互斥体可应用于不同进程之间;如,我们常使用互斥体对象实现应用程序的

唯一实例限制(不同进程之间):
首先在App类中定义互斥体对象  HANDLE hMutex;
在App::InitInstance的适当位置,添加如下代码:
 hMutex =  CreateMutex(NULL, TRUE, "CUSTOM_MUTEX_NAME");
 if( hMutex != NULL )
 {
  if( GetLastError() == ERROR_ALREADY_EXISTS )
  {
   exit(0);
  }
 }
但好像一些较复杂的应用程序,可能也会不尽人意,具体什么情况忘记了,以后想起来了再补充;但我

想这种方法已经不错了!

 

关于临界区和互斥体说一个简单的例子:
大家都做过火车,火车厕所的使用相当于一个临界区资源,具有独占性,当一个人进去开始使用资源之前,他首先要将厕所的门锁上,使用完资源后,再将锁打开;特别是春运或5.1/10.1放假时,坐硬座的乘客应该深有体会!别人加锁之后,你只有等待!

 

四、信号量
常用于对访问某一共享资源线程数目的限制;常用到的Win32函数有:
CreateSemaphore(NULL, 10, 10, NULL);
//创建一个使用默认安全属性、初始线程计数是10、线程总数为10、无名称的信号量对象

你可能感兴趣的:(多线程,数据库,File,null,mfc,FP)