在Outlook的一个插件中,我有一个工作线程进行一些处理,然后更新一个布尔标志。主线程检查此标志,如果这是假的,它只处理一个while循环并且什么都不做。
//worker thread void DoSoneThing() { Outlook.Recipients recps = mail.Recipients. foreach(Outlook.Recipient recp in recps) { //Save each recipients in a colection } isDone=true; } //Main thread while(!isDone) { //read the collection where recipients name have been stored. }``
如果主线程在工作线程将标志设置为true之前进入这段代码,则主线程继续处理循环,而第二线程只是暂停。由于isDone标志永远不会设置为true,因此主线程不做任何事情。
当我在DoSomeThing方法中设置锁并在mian线程中使用相同的锁时,此问题已得到解决。
myClass { public static object _syncRoot = new Object(); void DoSoneThing() { lock(_syncRoot) { //process isDone=true; } } } myOtherClass { lock(myClass._syncRoot) { //process } }
我的理解是锁用于限制多个线程进入同一段代码。但是不明白为什么当主线程访问共享资源时工作线程不做任何事情。
答案 0 :(得分:2)
我认为这里可能存在轻微的概念问题。
首先我可以建议
while(!isDone)
不是一种很好的等待方式 - 它被称为“旋转”并允许线程在没有做任何事情时使用处理器时间,这是无效的。 (在锁定时旋转在某些特定情况下可以正常,但在用户应用程序中通常不是一个好计划。)
锁定,让一个线程等待,而其他进程要好得多。
现在,关于你的具体问题。在while测试中读取的isDone标志可能已被优化(即编译器'知道'它不会改变,所以它没有放入任何代码再次从内存中获取它 - 它只是测试相同的CPU寄存器。)你可以通过使用'volatile'修饰符告诉编译器它必须从内存中重新获取值来克服这个问题。 也有可能主线程旋转,正在挨饿另一个线程,所以它永远不会有机会设置标志(尽管人们希望它最终会在'公平'系统中。)
无论后者如何,都要避免在等待另一个线程完成进程时进行线程旋转,除非检查了该标志,否则它会执行一些有用的操作(例如更新GUI)。
你需要对多线程设计非常小心 - 它们是错综复杂的,并且可能具有模糊和难以预测的行为(但在适当的情况下非常值得做。)