gpt4 book ai didi

c# - 从另一个线程调用方法而不阻塞线程(或为非 UI 线程编写自定义 SynchronizationContext)C#

转载 作者:行者123 更新时间:2023-11-30 21:51:36 25 4
gpt4 key购买 nike

这可能是 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.TaskBackgroundWorker 或任何其他独立的东西,如果有或没有 UI 线程(例如控制台应用程序)。

下面是示例代码,我尝试用它来测试异常的捕获(它清楚地表明错误的线程抛出了异常)。我将把它用作具有所有锁定功能的实用程序,检查线程是否正在运行等。这就是我创建类实例的原因。

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() 没有,finally 被调用。也许有人可以评论这种差异的原因。

最佳答案

and not the way how UI thread handles this issue

这不准确,正是 UI 线程处理它的方式。消息循环是 producer-consumer problem 的一般解决方案.在典型的 Windows 程序中,操作系统和其他进程产生消息,而唯一的 UI 线程使用消息。

需要此模式来处理从根本上讲线程不安全的代码。而且周围总是有很多不安全的代码,代码越复杂,线程安全的可能性就越低。您可以在 .NET 中看到,很少有类在设计上是线程安全的。很简单的事情是 List<> 不是线程安全的,您可以使用 lock 关键字来保证它的安全。 GUI 代码非常不安全,再多的锁定也无法使其安全。

不仅仅是因为很难弄清楚将 lock 语句放在哪里,还有一堆代码不是你写的。就像消息 Hook 、UI 自动化、将对象放置在您粘贴、拖放的剪贴板上的程序、当您使用 OpenFileDialog 等 shell 对话框时运行的 shell 扩展。所有这些代码都是线程不安全的,主要是因为其作者没有使其成为线程安全的。如果您在此类代码中遇到线程错误,那么您将没有电话号码可以调用,这将是一个完全无法解决的问题。

特定 线程上运行方法调用需要这种帮助。无论线程正在做什么,都不可能任意中断线程并强制它调用方法。这会导致可怕且完全无法调试的重入问题。像那种由DoEvents()引起的问题,但是乘以一千。当代码进入调度程序循环时,它隐含地“空闲”并且不忙于执行自己的代码。所以可以从消息队列中取出一个执行请求。这仍然可能出错,当您在空闲时抽水时,您会射断您的腿。这就是 DoEvents() 如此危险的原因。

所以这里没有捷径,您确实需要处理 while() 循环。这样做是可能的,这是您有充分证据证明的事情,UI 线程做得很好。考虑 creating your own .

关于c# - 从另一个线程调用方法而不阻塞线程(或为非 UI 线程编写自定义 SynchronizationContext)C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35500730/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com