实现此目的的最佳方法是:主线程(线程A)创建另外两个线程(线程B和线程C)。线程B和C执行繁重的磁盘I / O并最终需要将它们创建的资源传递给线程A然后调用外部DLL文件中的方法,该文件需要正确调用创建它的线程,因此只有线程A可以调用它
我使用线程的唯一另一次是在Windows窗体应用程序中,调用方法就是我需要的。此程序不使用Windows窗体,因此没有使用Control.Invoke方法。
我在测试中注意到如果在线程A中创建了一个变量,我可以从线程B / C访问和修改它,这对我来说似乎非常错误。使用Winforms,我确信它会尝试访问在其他线程上创建的内容时出错。我知道从多个线程更改内容是不安全的,但我真的希望.NET完全禁止它以确保安全编码。 .NET是否会这样做,而我只是错过了这条船,还是仅仅使用WinForm应用程序?
因为它似乎允许这样做,我是否会像操作系统那样做,创建一个标志并从线程A监视它以查看它是否发生变化。如果是,则调用该方法。事件处理程序实际上不是这样做的,所以可以在主线程上以某种方式调用事件吗?
答案 0 :(得分:4)
通常,这是不必要的。你可以从任何线程调用任何对象的方法,这是一件好事...... UI组件和一些遗留的COM组件往往是必须从特定线程访问的唯一项目。
通常,您不会尝试在不同的线程上调用方法,而是尝试使用同步(即:lock(...)和类似方法)来保护对数据本身的访问,并使其安全工作来自多个线程。
为您提供更细粒度的保护,这对性能有益。使用Control.Invoke实际上非常昂贵,因为它使用Windows消息传递来标记它需要运行的线程。
话虽这么说,实际上可以做到这一点,虽然很难。 “技巧”是你可以用它自己的线程创建一个对象,并让该对象提供SynchronizationContext。然后,您可以使用SynchronizationContext.Post或Send在该上下文中运行方法。
这很难做到,并且没有理由这样做,所以正如我所说,我不建议在大多数情况下使用它。
答案 1 :(得分:0)
“我确信它试图访问在其他线程上创建的内容时会出错。”此语句仅对控件的属性有效。可以从任何线程访问通用变量。是的,如果做错了会导致问题(竞争条件),但有时候是必要的。您应该将此变量标记为volatile,最小化对它们的访问,使用锁或Interlocked类。这是我的建议:))
答案 2 :(得分:0)
首先,“Winforms”不会仅仅为了尝试从另一个线程访问数据而抛出错误。那是不可能的;没有办法知道哪个线程最初声明了某个变量或创建了一些数据。 做什么导致Winforms中的异常是尝试从除了UI线程之外的任何线程调用Control
后代上的方法 - 这是一个非常特殊的特例。除此之外,Winforms只是一个运行在与任何其他.NET应用程序完全相同的运行时之上的库,并且无法对如何管理线程进行特殊控制。
.NET Framework或任何其他库绝对没有办法“确保”安全的多线程代码。某些概念有助于减少竞争条件和其他并发错误,例如.NET 4的并发集合库,不可变数据类型等,但您可以使用它们。编写多线程代码时,您将承担与之相关的所有风险和责任。程序员应该你来确保代码是线程安全的。
您在这里提到的具体案例似乎相当简单。我喜欢使用线程池,因此我将向您展示使用事件的解决方案;您也可以使用实际Thread
个对象执行此操作并使用Thread.Join
。假设我们在线程A:
void Xyz()
{
SomeData dataFromB = null;
ManualResetEvent finishedB = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(s =>
{
dataFromB = GetDataFromB();
finishedB.Set();
});
SomeOtherData dataFromC = null;
ManualResetEvent finishedC = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(s =>
{
dataFromC = GetDataFromC();
finishedC.Set();
});
finishedB.WaitOne();
finishedC.WaitOne();
finishedB.Dispose();
finishedC.Dispose();
CallExternalDLL(dataFromB, dataFromC);
}
这将阻止整个操作完成。如果直接从UI线程运行它,则应该从另一个工作线程调用整个方法。
答案 3 :(得分:0)
从我的pov中,最简单的方法是使用同步队列,其中B和C将项目排队。
然后,线程A将从队列中出列,并可能等到新的队列出现。
看看Doug在这个MSDN article中的评论作为这种队列的一个简单例子。
您还可以将代理排入队列,以确保代码在正确的线程中运行。