2012-04-03 123 views
1

这可能是一个愚蠢的问题,如果这已经回答了其他地方,那么我真的很感激,如果我的搜索没有止跌回升有人可以点我给它任何明确的。的Thread.join在UI线程阻塞也子线程


概括地说,我的问题是,当我在已标记停止childThread一个子线程UI线程做childThread.Join()似乎要禁止以及主线程所以一切只是挂起。
由于使用Join会阻塞UI,因为childThread在被告知退出之后应该在一秒钟内完成,因此此刻本身并不是问题。
发生这种情况时,我正在等待运行重复进程的线程退出,然后才能运行另一种返回某些信息但不能与其他进程同时运行的方法。

我的Winforms申请通过pinvoking为硬件中的C API用一块USB硬件的集成。

硬件API具有将开始,将无限期地重复运行,并迅速与新的信息,然后我需要传递到UI回调的过程的方法。
此操作可以通过对硬件API的另一个调用取消,该硬件API设置硬件可以看到的标志,以便它知道要退出。
我包裹着我自己的C#代码这个C API和包装中的我不得不分拆启动过程调用在另一个线程,这样的活动不会阻止用户界面。

这里有大约我在做什么编辑的亮点。

public class DeviceWrapper 
{ 
    Thread childThread = null; 

    void DeviceWrapper 
    { 
     //Set the callback to be used by the StartGettingInformation() process 
     PInvokeMethods.SetGetInformationCallback(InformationAcquiredCallback); 
    } 

    public void StartProcess() 
    { 
     childThread = new Thread(new ThreadStart(GetInformationProcess)) 
     childThread.Start(); 
    } 

    void GetInformationProcess() 
    { 
     PInvokeMethods.StartGettingInformation(); 
    } 

    //This callback occurs inside the childThread 
    void InformationAcquiredCallback(Status status, IntPtr information) 
    { 
     //This callback is triggered when anything happens in the 
     //StartGettingInformation() method, such as when the information 
     //is ready to be retrieved, or when the process has been cancelled. 
     if(status == Status.InformationAcquired) 
     { 
      FireUpdateUIEvent(); 
     } 
     //If the cancel flag has been set to true this will be hit. 
     else if(status == Status.Cancelled) 
     { 
      //Reset the cancel flag so the next operation works ok 
      PInvokeMethods.SetCancelFlag(false); 

      childThread.Abort(); 
     } 
    } 

    //This method runs once, and can't run at the same time as GetInformationProcess 
    public string GetSpecificInformation() 
    { 
     //This triggers InformationAcquiredCallback with a status of Cancelled 
     StopProcess(); 

     if(childThread.IsAlive) 
     { 
      childThread.Join(); 
     } 

     return PInvokeMethods.GetSpecificInformation(); 
    } 

    public void StopProcess() 
    { 
     PInvokeMethods.SetCancelFlag(true); 
    } 
} 

使用当我打电话childThread.Join()整个应用程序嘎然而止(这是我所期待的UI,这就是罚款)和childThread也似乎停止,因为回调从来没有得到这个代码再次击中。

不过,如果我使用下面的代码来代替:

public string GetSpecificInformation() 
{ 
    //This triggers InformationAcquiredCallback with a status of Cancelled 
    StopProcess(); 
    string s = ""; 

    ThreadPool.QueueUserWorkItem(new WaitCallback(delegate 
    { 
     if(childThread.IsAlive) 
     { 
      childThread.Join(); 
     } 
     s = PInvokeMethods.GetSpecificInformation();    
    })); 

    return s; 
} 

然后,一切都被击中预期和childThread确实完成,一切都很好,但显然我的字符串得到WaitCallback火灾前返回空并分配到它。

所以,我只是要吮吸它,并更改类,所以我使用QueueUserWorkItem和WaitCallback和触发事件来处理我的字符串返回?
在我的第一个方法中,是否有一些愚蠢的行为导致childThread阻塞?
还是有另一种策略或类,我应该使用,铭记它是.NET 3.5我在?

+3

我花了近30年的时间试图阻止使用TThread.WaitFor()和其他类似的硬锁同步机制来产生死锁的Delphi开发人员。就在我想到我到某个地方时,Java和C#开发人员发现了join()。永无止境的噩梦。 – 2012-04-03 16:06:18

+0

相信我,除非我绝望而且不在乎自己的深度,否则我不会触及任何这些;)那么你会推荐什么呢? – Nanhydrin 2012-04-03 16:12:38

+0

如果你听Eric和其他人的话。 Invoke,BeginInvoke等,你不会错误的。不要在事件处理程序中等待 - 对其他线程有话要说的代理在主线程上触发时返回的信号采取行动。 – 2012-04-03 17:16:01

回答

5

那么,FireUpdateUIEvent();听起来像一种可能的方法 发布 发送到MsgQueue(Control.Invoke())。当主线程在Join()中等待时,那么你有一个典型的死锁。

另外,childThread.Abort()不被认为是安全的。

那么,我只需要吸取它并更改类,以便使用QueueUserWorkItem和WaitCallback并触发一个事件来处理我的字符串返回?

我当然会重新设计它。它可能可以简化一点。

+0

FireUpdateUIEvent不会直接更新UI本身,但UI一定会为它提供处理程序,并且该回调将阻止等待它返回。但是由于它实际上并没有返回任何值得关注的价值,我可以想象将它转换成另一个线程,或者我应该做的更好吗? – Nanhydrin 2012-04-03 16:09:54

+0

@Nhydhydrin:Henk是对的;你应该重新设计整个事情。你不应该在UI线程中做线程中止或连接。如果你想告诉UI线程在另一个线程上发生了一些有趣的事情,请执行与操作系统相同的操作:将消息放入其队列中。 – 2012-04-03 16:12:55

+0

FireUpdateUIEvent应该使用Control.BeginInvoke,而不是Control.Invoke。这可能会解决僵局。但是你仍然会有Join()和Abort(),这不是一个好的情况。 – 2012-04-03 16:12:55