C#Basic多线程问题:线程B中线程A的调用方法(线程B从线程A开始)

时间:2010-03-18 15:20:33

标签: c# multithreading controls invoke

实现此目的的最佳方法是:主线程(线程A)创建另外两个线程(线程B和线程C)。线程B和C执行繁重的磁盘I / O并最终需要将它们创建的资源传递给线程A然后调用外部DLL文件中的方法,该文件需要正确调用创建它的线程,因此只有线程A可以调用它

我使用线程的唯一另一次是在Windows窗体应用程序中,调用方法就是我需要的。此程序不使用Windows窗体,因此没有使用Control.Invoke方法。

我在测试中注意到如果在线程A中创建了一个变量,我可以从线程B / C访问和修改它,这对我来说似乎非常错误。使用Winforms,我确信它会尝试访问在其他线程上创建的内容时出错。我知道从多个线程更改内容是不安全的,但我真的希望.NET完全禁止它以确保安全编码。 .NET是否会这样做,而我只是错过了这条船,还是仅仅使用WinForm应用程序?

因为它似乎允许这样做,我是否会像操作系统那样做,创建一个标志并从线程A监视它以查看它是否发生变化。如果是,则调用该方法。事件处理程序实际上不是这样做的,所以可以在主线程上以某种方式调用事件吗?

4 个答案:

答案 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中的评论作为这种队列的一个简单例子。 您还可以将代理排入队列,以确保代码在正确的线程中运行。