多线程处理:食堂进餐问题

来源:互联网 发布:淘宝官网软件 编辑:程序博客网 时间:2024/04/27 21:13

前面提到,调用TerminateThread终止线程曾导致栈变量回收出现问题,进而导致死锁。为了解决这个问题,只能等待各个子线程主动退出,由此导致资源访问的互斥问题。

为了解决这个问题,我建立了一个参考模型:食堂进餐。

由于食堂老板(应用程序主线程)很忙,所以安排了一个管理员(管理线程)来管理食堂。食堂每天中午12点到12点半开放。

起初,来食堂吃饭的人不多,而且附近也没有其他食堂,所以,管理员限定,每天12点半关门,12点半一过,不论是否吃完,一律不得在食堂停留。但是,后来人多了,这样处理就不方便了。每到12点半,很多人匆匆忙忙离去,食堂内碗筷被丢得乱七八糟。——这就是TerminateThread强制终止线程的效果。

为了解决这个问题,管理员限定,到了12点半,所有人(子线程)必须依次离开(线程终止)。此后相当长的时间内,一切正常。但是,管理员很快发现一个不好的现象。极少数人在十二点半才过来进餐,当他们买好饭菜、还没付款的时候,刚好到了食堂关门时间,于是这些人没有买单就匆匆忙忙离开。——这就是现在遇到的问题,临界区与资源回收冲突问题。

我想了想,觉得只有采用另外一种方法:所有的资源回收都由管理员线程完成。即,管理员在12点半通知所有的食堂工作人员停止工作,然后再通知用户(线程)离开(终止)。

关于该问题的补充说明:

1. 程序中使用了模板实现的链表,由于模板类的特殊性,不方便调试,所以,模板类中的一个隐藏的BUG一直没被发现。也正是由于这个BUG,间接导致了上面的问题。

2. 除(1)之外,线程间互锁是问题的关键。

(2.1)A占用资源C,同时等待资源D;B占用资源D,等待资源C。——这是一个简单的死锁模型。程序中出现了这种情况。

解决方法是,在A准备占用C资源之前,先解除锁定,再重新加锁,即可。

例如:
     if (thread_handle!=NULL)
     {
      m_lock_manager.Unlock();
      m_lock_manager.Lock();
      WaitForSingleObject(thread_handle, INFINITE);
     }

虽然,Unlock();再Lock();,这样的代码,看似啰嗦,事实上解决了死锁问题。

——这种方法只能最大可能地减小死锁的可能性,但是,不能彻底排除死锁。原因很简单,因为这里假设LockWaitForSingleObject是一个原子操作,但是,很显然,这只是我自己的一厢情愿,电脑并不会100%地按照这种设想的思路去执行。

不管怎么说,这种方法基本上可以解决问题了!一方面,应用层是无法实现原子操作的;另一方面,在Lock之后、WaitForSingleObject之前,另外一个线程恰好再次锁定临界区,这种可能性几乎可以忽略了,即使出现了,偶尔的进程崩溃是可以容忍的。而且,这只是一个初期的测试版本,后面如果想到更好的方法,再改动不迟。

(2.2)A、B、C三个线程互相抢占资源。

这种可能性比(2.1)更大,而程序中确实存在这一隐患。解决方法同上面所述,所有的资源回收都由管理员线程完成。这样,(2.2)就演化为(2.1)了。

当然,这些都不是最好的方法。——管理者线程不应该干预子线程的操作,不应该等待子线程结束,更不该回收子线程的堆内存资源。我原先的想法是,管理者管理一切,结果,管理者和技术人员意见不合了,死锁!管理者应该只负责调度和分配,具体到每个人,各人做各人的事情,做完后回收一下资源,就OK了。如果要下班了,管理者通知一声,仅此而已。

原创粉丝点击