feiyang025 发表于 2023-12-20 09:20:52

C++多线程编程经验—安全的线程暂停、恢复


多线程中,线程需要暂停的原因多种多样,基本分为两大类,1是等待数据,2是系统资源紧张,停止计算以把资源让给其他线程.

一般初学者会使用的方法就是使用SuspendThread和ResumeThread这两个API或者他们的包装函数进行操作,许多教材上都是这么说的,但这么作会带来及其严重的问题。

这两个API的问题是无法确定函数被暂停的位置,这是一个足以让任何多线程程序崩溃的问题。

想象一种情况,A线程控制B线程暂停和继续,AB共享一个变量C,C由互斥量D加锁使得其可以在AB间安全共享。

现在有一种最糟糕的情况,A暂停B的时候,B已经Lock了D,但是还没有Unlock.而A只有在Lock了D以后才能继续B,于是两个线程陷入死锁。

并且几乎无法稳定重现这个bug,在多数情况下,也许1000次只有一次会死锁,于是你的程序变成定时炸弹。

第二种情况更加常见,比如执行SQL查询的代码,获得了一个recordset的COM对象,于是通过while循环提取数据,在循环的过程中,线程被暂停。

过了一段时间线程继续执行,你立刻会收到一个异常:管道的另一端没有任何进程。相当的莫名奇妙,其实用COM执行SQL的时候,返回结果并没有移动到用户进程的内存空间,

而是SQL服务器保留了一个进程保存数据,但COM组件要求数据的时候再传送过去,太长时间没有数据提取要求,SQL服务器认为你的程序崩溃了,于是开始回收资源,

关闭进程,这时你的程序倒是继续执行了。这个异常即使捕获也几乎无法处理,要回滚程序状态,重新执行查询麻烦到极点,

你只有一条路,就是提醒用户程序出错了,我想用户的体验一定相当糟糕。


暂停线程必须想一个办法在能控制线程在能暂停的地方暂停,暂停点不能有任何数据已经上锁,上面说的那种while循环中也不能被暂停,

还有一种就是当有大量内存被申请且还没有释放之前最好也不要被暂停。我建议大家用手动模式的信号量来实现。

像这样申请一个信号量:
//第二个参数是TURE,设定为手动模式,防止::WaitForSingleObject改变信号量的状态。
hEvent=::CreateEvent(NULL,TURE,TURE,lpName);

在所有线程可以被暂停的地方加上:
::WaitForSingleObject(hEvent,-1);


这样,只需要用这两个函数
::SetEvent(hEvent);//继续
::ResetEvent(hEvent);//暂停

发出暂停和继续的命令就可以了。

线程只会停在你认为能停的地方,可以避免上述两种类型的BUG,不只是C++,所有语言都可以使用这个思想。
页: [1]
查看完整版本: C++多线程编程经验—安全的线程暂停、恢复