Передача объектов между потоками вызывает исключение, потому что другой поток владеет им - PullRequest
1 голос
/ 06 октября 2011

У меня раньше была такая проблема с обновлением элементов графического интерфейса через события, возникающие из пользовательских классов, но я смог обойти это с помощью чего-то вроде:

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

Я думал, что эта проблема уникальна для потоков пользовательского интерфейса, но, очевидно, она применима ко всем потокам. И, к сожалению, мои пользовательские классы не имеют подпрограммы .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 () - это то, где я получаю исключение «Вызывающий поток не может получить доступ к этому объекту, потому что другой поток владеет им». Мне нужно иметь доступ к этому свойству и свойствам и методам ниже. Что мне нужно сделать, чтобы получить доступ к тому, что мне нужно?

1 Ответ

2 голосов
/ 06 октября 2011

Я бы посмотрел на SyncronizationContext , предоставленный System.Threading.У этого класса есть статическое свойство Current, которое обеспечит запрашиваемый стиль Dispatcher.

Другие возможности

Если вы хотите обрабатывать события впоток, отличный от того, что создает ваш класс JobData, вам может потребоваться переместить фактический поиск PrintSystemJobInfo в обработчик событий.

Другой альтернативой может быть абстрагирование класса System с помощью нового класса, который высоздать (без привязки к потоку).

...