在线程之间传递对象会导致异常,因为其他线程拥有它

Passing objects between threads causes exception because a different thread owns it

提问人:Ultratrunks 提问时间:10/7/2011 更新时间:10/7/2011 访问量:1209

问:

我以前遇到过这种问题,通过从自定义类引发的事件更新 GUI 元素,但我能够通过以下方式解决它:

MyTextBlock.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
      (Action)(() => { LoggingTextBlock.Text += Message += "\r\n"; }));

我认为这个问题是UI线程独有的,但显然它适用于所有线程。不幸的是,我的自定义类没有 .Dispatcher() 例程调用,就像 UIElements 一样。那么,您应该如何将对象传递给其他线程并让它们能够使用它们呢?

例如,我有一个基侦听器类,其主要工作基本上是查找一些数据,引发事件并传递该数据。以下是我想传递的数据类的一部分:

// Just the class for carrying the job data.
public class JobData
{
    // NOTE: I don't no have trouble accessing these properties from a 
    // different thread
    public string SomeProperty1 { get; set; }
    public string SomeProperty2 { get; set; }
    public string SomeProperty3 { get; set; }

    // ...
    // more properties
    // ...

    // This a .NET object of type System.Printing.PrintSystemJobInfo.  This
    // is the guy that gives me trouble.  Later on, I can't access members 
    // of Job without getting the "The calling thread cannot access this 
    // object because a different thread owns it" error.
    PrintSystemJobInfo m_Job = null;
    public PrintSystemJobInfo Job
    {
        get
        {
            if (m_Job == null)
            { throw new ArgumentNullException(); }

            return m_Job;
        }

        set { m_Job = value; }
    }
}

下面是类的一个段,它在线程中运行,收集数据并触发事件以将该数据传递给任何侦听者。

// Thread who monitors looking for data to package into JobData and send
// to any listeners.
public class JobMonitor
{
    public delegate void NewJobEvent(JobData jobStuff);
    public event NewJobEvent NewJob;

    // Call this when you have some job data and need to notify listeners
    private void OnNewJob(object newJobData)
    {
        JobData newJobData = (JobData)newJobData;
        if (NewJob != null)
        {
            NewJob(newJobData);
        }
    }

    private void WorkerRoutine()
    {
        while(true)
        {
            // Wait for data

            // ...
            // ...

            // when data is found

            JobData myJobData = new JobData();
            myJobData.SomeProperty1 = "some data";
            myJobData.SomeProperty2 = "some data";

            int jobID = GetCurrentJobID();
            string printerName = GetCurrentPrinterName();

            // .NET class System.Printing.PrintQueue
            PrintQueue printQueue = new PrintQueue(new PrintServer(), printerName);

            PrintSystemJobInfo jobInfo = null;
            jobInfo = printQueue.GetJob(jobID);

            // This is the guy in my JobData class that I'll have trouble accessing
            // later on.
            myJobData.Job = jobInfo;

            // Time to fire off the event
            OnNewJob(myJobData);
        }
    }
}

下面是实例化 JobMonitor 类并捕获其事件的类的段。它在访问 JobMonitor 的某个属性时遇到问题。

// This class signs up to recieve and process events from the JobMonitor class.
public class JobProcessor
{
    // this is the method that handles the incoming job events.  This is where 
    // i have trouble.
    private void NewJobEventHandler(JobData newJob)
    {
        string temp = newJob.SomeProperty1; // this works fine

        bool bTemp = newJob.Job.IsPaused;   // this throws an exception "The
                                            // calling read cannot access this
                                            // object because a different thread
                                            // owns it"
    }

    private JobMonitor m_monitor = null;
    public JobProcessor()
    {
        m_monitor = new JobMonitor();

        //attaches a method to handle incoming job events.
        m_monitor.NewJob += new JobMonitor.NewJobEvent(NewJobEventHandler);
        m_monitor.Start();
    }
}

因此,JobProcessor.NewJobEventHandler() 方法是我收到异常的地方:“调用线程无法访问此对象,因为其他线程拥有它”。我需要能够访问此属性以及下面的属性和方法。我需要做些什么才能访问我需要的内容?

C# 多线程 事件 参数传递

评论


答:

2赞 davisoa 10/7/2011 #1

我会研究 System.Threading 提供的 SyncronizationContext。此类具有一个静态属性,该属性将提供您正在寻找的 Dispatcher 样式调用。Current

其他可能性

如果要在与创建 JobData 类的线程不同的线程上处理事件,则可能需要将 的实际检索移动到事件处理程序。PrintSystemJobInfo

另一种选择是用您创建的新类(没有线程亲和性)抽象出 System 类。

评论

0赞 Ultratrunks 10/11/2011
好吧,我等了一会儿,看看是否有其他建议可能会出现。“SyncronizationContext”方法对我来说没有结果。我对这个概念有点迷茫,所以我可能做错了。幸运的是,检索 PrintSystemJobInfo 对象很容易,并且有足够的信息让我的目标线程自行获取此对象。理想情况下,我不必这样做,我的目标线程也不需要知道如何获取此信息。哦,好吧,从长远来看,这对我来说并不是一个很大的妥协。