请教高手:对同一个缓冲区,一个线程往里写数据,一个从中读取数据,需不需要做临界区同步处理?

feijj2002_ 2007-11-09 09:50:13
请教高手:对同一个缓冲区,一个线程往里写数据,一个从中读取数据,需要做临界区同步处理吗?
...全文
1394 30 打赏 收藏 转发到动态 举报
写回复
用AI写文章
30 条回复
切换为时间正序
请发表友善的回复…
发表回复
D_magic 2011-07-19
  • 打赏
  • 举报
回复
如果要求写入之后再读出就需要个同步信号量来处理,还要考虑缓冲区是否安全即是否需要互斥保护,看自己的设计了。
据说有个圆形缓冲区,可以不用保护,圆形缓冲区原理是什么呢?
majingxiong 2011-07-01
  • 打赏
  • 举报
回复
这个当然需要
夹道穿行 2007-12-23
  • 打赏
  • 举报
回复
当然要啦,在多CPU中不加同步,会出问题的
sxcong 2007-11-27
  • 打赏
  • 举报
回复
想都不用想,当然要。否则所有的OS都设置的同步事件给谁用呢
feijj2002_ 2007-11-24
  • 打赏
  • 举报
回复
结贴前,顶下/
stivenjia 2007-11-13
  • 打赏
  • 举报
回复
DWORD CALLBACK DirWatchObject::ThreadProc(LPVOID pVoid)
{
DWORD dwByt = 0,dwEvent = 0;
DirWatchObject *pT = static_cast<DirWatchObject*>(pVoid);
OVERLAPPED vrlp = { 0 };
BOOL bRz = FALSE;
vrlp.hEvent = pT->m_hEvent_Add[0];
BYTE Byte_NF[4096] = { 0 };
PFILE_NOTIFY_INFORMATION pFileNotifyInfo = NULL;
LPDIRINFO lpDirInfo = NULL;
CString sz;
USES_CONVERSION;
LOOP:
ZeroMemory(Byte_NF,sizeof(Byte_NF));
bRz = ReadDirectoryChangesW(pT->m_hFl,(void*)Byte_NF,sizeof(Byte_NF),FALSE,pT->m_dwAction,
&dwByt,&vrlp,NULL);
dwEvent = WaitForMultipleObjects(2,pT->m_hEvent_Add,FALSE,1000);
switch(dwEvent)
{
case WAIT_FAILED:
OutputDebugString("ThreadProc Error \r\n");
break;
case WAIT_OBJECT_0 +0:
pFileNotifyInfo = (PFILE_NOTIFY_INFORMATION)Byte_NF;
if(pFileNotifyInfo->NextEntryOffset != 0)
{
pFileNotifyInfo += pFileNotifyInfo->NextEntryOffset;
}
lpDirInfo = (LPDIRINFO)malloc(sizeof(DIRINFO));
lpDirInfo->dwAciton = pFileNotifyInfo->Action;
strcpy(lpDirInfo->szFileName,W2A(pFileNotifyInfo->FileName));
pT->m_listDir.push_back(lpDirInfo);
sz.Format("%d\r\n",pT->m_listDir.size());
OutputDebugString(sz.GetBuffer(0));
SetEvent(pT->m_hEvent_Del[0]);
break;
case WAIT_OBJECT_0 +1:
OutputDebugString("Add Closed \r\n");
SetEvent(pT->m_hEvent_Add[2]);
return 2;
}
goto LOOP;
return 1;
}
DWORD DirWatchObject::CallUserProc(LPVOID lpvoid)
{
DirWatchObject *pT = static_cast<DirWatchObject*>(lpvoid);
UINT unLen = 0;
DWORD dwEvent = 0;
LOOP:
dwEvent = WaitForMultipleObjects(2,pT->m_hEvent_Del,FALSE,INFINITE);
unLen = pT->m_listDir.size();
list<LPDIRINFO>::iterator itBin = pT->m_listDir.begin();
list<LPDIRINFO>::iterator itOldBin = itBin;
list<LPDIRINFO>::iterator itEnd = pT->m_listDir.end();
switch(dwEvent)
{
case WAIT_FAILED:
OutputDebugString("Del Thread Error \r\n");
break;
case WAIT_OBJECT_0 +0:
while(TRUE)
{
itEnd = pT->m_listDir.end();
if(itBin != itEnd )
{
pT->m_CallProc((*itBin)->szFileName,strlen((*itBin)->szFileName),(*itBin)->dwAciton,pT->m_pvoid);
}
else
{
pT->m_listDir.clear();
break;
}
delete *itBin;
itBin++;
}
break;
case WAIT_OBJECT_0 +1:
OutputDebugString("Del Closed \r\n");
SetEvent(pT->m_hEvent_Del[2]);
return 3;
}
goto LOOP;
}
大体上就这个意思
goodboyws 2007-11-13
  • 打赏
  • 举报
回复
这个要看写数据有没有完整性要求,比如先写BufferSize,再写一整块数据,这时候就需要同步
zaodt 2007-11-13
  • 打赏
  • 举报
回复

这是一个生产者和消费者问题。

我们先不考虑程序怎么实现,只考虑这个问题。

另外,我们从理论上分析,看看所有可能的情况。

建立一个环形的队列,这个队列不可能是无限大的,它一定是有限大的,否则也不用环形了。

1、如果写数据的线程速度比较快些的话,那么它有可能会与读线程的距离越来越远,甚至可能追上它一圈;

2、如果读数据的线程快的话,那么它有可能超越写线程,那它读的都是空数据;

3、理想情况是:写线程比读线程快一格,写线程始终都在前边,但读线程也始终保持着与写线程差一格;

现在看实际的情况:

1、当读写线程都是在同一台计算机中操作,很有可能达到某种平衡,就是说它们可以工作的很好;

2、但是,当写线程写入的数据是从网络过来的,这就不能保证它们之际的平衡了;

比如,在做实时语音传输时,一个线程接收网络传来的语音数据放入环形队列,而另一个线程从对了中读取数据进行播放。

1、当网络好时,可以达到某种平衡;
2、当网络拥挤时,很有可能在一瞬间,将没有数据可读;而又因为是实时语音,又不可能过多的缓存数据,所以这个时候读线程就要停止,等待数据到来。
3、还有一种情况就是,网络来的数据太快,本地播放要慢,因为是实时语音,这个时候读线程就要向前跳跃,否则语音就要滞后了。


综上所诉,不难看出,写线程和读线程不可能同时读一块缓存,因为它们也不应该读一块缓存,读线程总应该比写线程慢一些才对,所以说,加临界区是没有必要的,而且加了反而影响效率!

虽然说不加临界区了,但写与读的动态平衡要比加临界区还要复杂,处理好这个动态平衡才是最关键的!
feijj2002_ 2007-11-13
  • 打赏
  • 举报
回复
to zaodt,谢谢热心回复.

两个线程的效率平衡,处理是比较复杂.在实际开发中也遇到这样的问题.
我们目前的产品,就是通过TCP网络传过来的数据,网络系统需要日夜长时间不间断地运行,所以,负载平衡和线程之间读写处理效率,特别难以处理.对于网络负载比较高的情况,只能尽量使用较高配置的主机做处理平台,如果还不够平衡,对于网络来的数据只得以丢弃处理,当然是在不影响客户关键业务信息的情况下.

zhoujianhei 2007-11-13
  • 打赏
  • 举报
回复
没有必要加,不要做无用功。
感叹一下,只有懒人才能成为最好的程序员。
另外少写点代码对日后的维护,那是相当有好处啊。

以上纯属个人拙见,楼主谨慎。
feijj2002_ 2007-11-13
  • 打赏
  • 举报
回复
感谢LS上面各位的热心回复.

希望通过与大家的交流中获得学习和提高.

我个人所了解的,在多线程中,对于互斥也好,临界区也好,最常见的一个作用就是:当存在数据相关性时候,用于保证当前所访问内存单元数据的可靠性和正确性.
正如通常教材所讲的,两个线程序对同一个内存单元同时访问(读或写)时,如果不加临界或互斥处理,就可能出现读的数据,并不是真正的新写后的数据的现象。
以上基于相关性及数据正确性的考虑,需要加临界或互斥处理.这种情况,也是我们开发过程中,最常见的一种.

另外还有一种情况,就是一些类如系统资源或系统对象,这类对象,当前能且只能被"一个"线程占用或访问,因为这些资源通常由一些全局变量在控制,如果另一个线程执行访问这些变量,可能会修改当在当前线程中起作用的全局变量值,从而可能会引起线程执行异常,这种情况,也必需加临界或互斥.

以上两种情况,都是为了保证数据的相关性及正确性,为避免"两个线程同时操作内存导致数据值的不实时、可靠"引起的程序非正常执行,才加了临界或互斥处理.

现在,如果撇开线程所操作内存数据的正确性,也就是不考虑两个线程同时操作同一个内存的所获数据是否正确(除系统级对象之外),也不加互斥的情况下,两个线程序同时操作内存,会不会导致系统出错或程序无踪影地异常退出。

比如,一个线程aThread()改写某一个内存单元内值:如:
a=10;
如果该语句在CPU执行序列中分为4个CPU时钟周期完成,而在执行到第二时钟周期时,该线程被调度,停止执行,操作系统将CPU执行权让给另外一个线程(bThread)。此时,bThread线程也要改写内存单元a内的值,这时候,会不会出现
:“由于aThread线程对内存单元a操作未完成(4cpu周期只执行了2个周期),CPU会不会对a内存的访问权限进行控制或类似锁定的处理,从而导致bThread线程访问a内存命令执行失败”的现象。

也就是该语句a=10,在CPU中,是不是不可调度的原子操作。
如果是原子操作,就可得出:如果a不存在资源的独占性,也不存在数据的实时正确性和相关性,就不需加互斥和临界处理。
如果不是原子操作,并且有上面说的硬件保护或锁定,就得出:不管a是否独占性,还是有没有数据相关性,只要两个线程序同时访问,就必须加互斥或临界处理。




对线程认识粗浅,望能得到各位的交流和指点。
guo_wei 2007-11-13
  • 打赏
  • 举报
回复
可以这么做,但这绝对不是好的方法和习惯,对于多线程同时操作的内存,无论是否会有影响都应该加上保护。
thisisyjs 2007-11-13
  • 打赏
  • 举报
回复
同一块数据的同时读写是需要保护的,如果是32位的整型数据有可能在整个数据还没完全写入缓冲区的时候读取到错误的数据,不用临界区可以,但至少要使用原子操作,保证不会读入错误的数据如: InterlockedExchange.
feijj2002_ 2007-11-12
  • 打赏
  • 举报
回复
应用场景大致是这样的:

定义一个大数组BYTE data[1024*1024*20],正如上面一位所说的一样,该数组当作"环形缓冲区"用.

现在有两个线程对这个缓冲区进行并行访问,
其中一个线程(写线程)负责将TCP接收的数据"写入"data数组中.
另外一个线程(读线程)负责从缓冲区中"读出"数据,同时并对读出的数据做其他的运算或处理.

"写线程",主要是做如下处理:(如下示例代码,未考虑数组的边界)
1.首先判断缓冲区是否已满, 如果满,则将pos回到缓冲区头(pos = 0;),并将数据写入数组.如果没满,则将数据追加到缓冲区中(data[pos] = bNewData;pos++;).
2. 数据写完后,发事件给"读线程",通知读数据.setEvent(hEvent);

(写线程没有做任何互斥或临界区处理)

"读线程",主要做如下处理:
1.收到写线程发出的事件后(waitForSingleObject).
首先获取"已写位置"的值 endpos = pos;
判断"已读位置readedPos"和"已写位置endpos"是否相等
如果不相等(readedPos != endpos ),则有新数据,则获得数据
(如下代码,对数组的边界和循环没有考虑)
for(int i = readedpos ; i < endpos;i++ )
{
temp = data[i];
}
将"已读位置"设置为endpos
readedpos = endpos;

如果相等,表示没有新数据,继续等待.

2.由于,读线程在读数据同时,"写线程"也会在写数据,同时改变pos值.所以,以上完成读处理后,仍然继续判断readedPos 和pos,如果相等,表示没新数据,则回到waitForSingleObject处等待.如果有新数据,回到第1步继续出来.
这样循环处理.

(对读线程,同样没有加任何的互斥和临界区处理)


由于,"写线程"只负责pos的修改,"读线程"只负责readedpos的修改,对环形缓冲区的访问不存在两个线程同时访问或修改同一个内存单元的情况.,应该不会存在冲突,所以,没有加临界区处理.这样做,安不安全?

daidongsheng 2007-11-12
  • 打赏
  • 举报
回复
不存在 "读的并不是写完的正确的数据 "的情况---->就是说读的都是写完的正确数据了

记录缓冲区的读写位置信息变量---->相当于实现了信号量的功能

其他的我认为就不需要了
TianChong 2007-11-12
  • 打赏
  • 举报
回复
应该加同步处理,因为对于一个数组,你"读"到最后一项时,可能认为已没有了,但实际上你判断的一刹那,"写"操作完成了一半,实际上你刚判断完毕,就有一条新记录了,这在平时极难发生,但时间久了总会发生的.当然情况还不止这些,对于读写并存的操作,坚决加上同步处理,性能影响当然有,但你又不是多线程,对于单线程来说,几乎为零,量化的话,就是:你有一万元,现在少了五分钱.
losky 2007-11-12
  • 打赏
  • 举报
回复
多个线程访问同一资源当然要同步了
jacklzw88 2007-11-12
  • 打赏
  • 举报
回复
最好加,至于性能不太会受到影响的。两个线程只是相对的而言的单独执行,你个线程不执行,那就会执行另一个线程。
tccqs 2007-11-12
  • 打赏
  • 举报
回复
没试过,深切关注中!!
V若只如初见 2007-11-12
  • 打赏
  • 举报
回复
很显然要加的,只要是多线程操作一个临界资源,处于安全考虑,都要对他进行加锁
加载更多回复(10)

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧