这可能是Stackoverflow中最常见的问题之一,但是我无法找到问题的确切答案:
我想设计一个模式,允许从线程A启动线程B并在特定条件下(例如,当发生异常时)调用线程A中的方法。如果异常,正确的线程很重要,因为异常必须调用主线程A中的一个catch方法。如果一个线程A是一个UI线程,那么一切都很简单(调用.Invoke()
或.BeginInvoke()
,那就是它)。 UI线程有一些机制如何完成,我想获得一些见解如何为非UI线程编写自己的机制。通常建议的方法是使用消息泵http://www.codeproject.com/Articles/32113/Understanding-SynchronizationContext-Part-II
但是while
循环会阻塞线程A,这不是我需要的,而不是UI线程处理这个问题的方式。有多种方法可以解决此问题,但我希望能够更深入地了解该问题并编写自己的通用实用程序,而不依赖于所选方法,例如使用System.Threading.Thread
或System.Threading.Tasks.Task
或{{1}或者其他任何东西,如果有或没有UI线程(例如控制台应用程序),则独立
下面是示例代码,我尝试用它来测试异常的捕获(这清楚地表明错误的线程抛出异常)。我将它用作具有所有锁定功能的实用程序,检查线程是否正在运行等等,这就是我创建类实例的原因。
BackgroundWorker
我发现的一个有趣的功能是,class Program
{
static void Main(string[] args)
{
CustomThreads t = new CustomThreads();
try
{
// finally is called after the first action
t.RunCustomTask(ForceException, ThrowException); // Runs the ForceException and in a catch calls the ThrowException
// finally is never reached due to the unhandled Exception
t.RunCustomThread(ForceException, ThrowException);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
// well, this is a lie but it is just an indication that thread B was called
Console.WriteLine("DONE, press any key");
Console.ReadKey();
}
private static void ThrowException(Exception ex)
{
throw new Exception(ex.Message, ex);
}
static void ForceException()
{
throw new Exception("Exception thrown");
}
}
public class CustomThreads
{
public void RunCustomTask(Action action, Action<Exception> action_on_exception)
{
Task.Factory.StartNew(() => PerformAction(action, action_on_exception));
}
public void RunCustomThread(Action action, Action<Exception> action_on_exception)
{
new Thread(() => PerformAction(action, action_on_exception)).Start();
}
private void PerformAction(Action action, Action<Exception> action_on_exception)
{
try
{
action();
}
catch (Exception ex)
{
action_on_exception.Invoke(ex);
}
finally
{
Console.WriteLine("Finally is called");
}
}
}
会抛出未处理的异常,new Thread()
从未被调用,而finally
则没有,new Task()
被调用。也许有人可以评论这种差异的原因。
答案 0 :(得分:3)
而不是UI线程处理此问题的方式
这不准确,它是完全 UI线程如何处理它。消息循环是producer-consumer problem的通用解决方案。在典型的Windows程序中,操作系统以及其他进程会生成消息,并且只使用一个唯一的UI线程。
这种模式需要处理从根本上说是线程不安全的代码。并且总是存在许多不安全的代码,它越复杂,它就越可能成为线程安全的。你可以在.NET中看到的东西,很少有类通过设计是线程安全的。一个简单的东西是List&lt;&gt;不是线程安全的,您可以使用 lock 关键字来保证其安全。 GUI代码非常不安全,没有任何锁定可以使其安全。
不仅因为很难找出放置 lock 语句的位置,还有一堆代码涉及到你没有编写。就像消息钩子,UI自动化,将对象放在剪贴板上的程序,当你使用像OpenFileDialog这样的shell对话框时,你粘贴,拖放,shell扩展。所有这些代码都是线程不安全的,主要是因为它的作者没有拥有来使其成为线程安全的。如果您在此类代码中查找了线程错误,那么您没有可以拨打的电话号码以及完全无法解决的问题。
在特定的线程上运行方法调用需要这种帮助。不可能随意中断线程,强制它调用方法。这导致了可怕的,完全无法解决的重新入侵问题。就像DoEvents()引起的那种问题,但乘以一千。当代码进入调度程序循环时,它隐式“空闲”并且不忙于执行自己的代码。因此可以从消息队列中获取执行请求。这仍然可能出错,当你不空闲时,你会在你抽水时射击你的腿。这就是DoEvents()如此危险的原因。
所以这里没有捷径,你真的需要处理while()循环。有可能这样做是非常可靠的证明,UI线程做得非常好。考虑creating your own。