Thread.Join в потоке пользовательского интерфейса также блокирует дочерний поток - PullRequest
1 голос
/ 03 апреля 2012

Это вполне может быть глупый вопрос, и если на этот вопрос уже был дан ответ в другом месте, я был бы очень признателен, если бы кто-то мог указать мне на это, поскольку мой поиск не дал ничего определенного.


В двух словах, моя проблема в том, что когда я выполняю childThread.Join () в потоке пользовательского интерфейса в дочернем потоке, который был помечен для остановки childThread, он блокируется так же, как и основной поток, поэтому все просто зависает.
То, что пользовательский интерфейс будет блокироваться из-за использования Join, в данный момент само по себе не является проблемой, поскольку childThread должен завершиться менее чем через секунду после того, как ему все равно будет приказано выйти.
Это происходит, когда я ожидаю завершения потока, выполняющего повторяющийся процесс, прежде чем я смогу запустить другой метод, который возвращает некоторую информацию, но не может быть запущен одновременно с другим процессом.

Приложение My Winforms интегрируется с частью USB-оборудования путем привязки C API к оборудованию.

В аппаратном API есть метод, который запускает процесс, который будет выполняться бесконечно, многократно и быстро, с обратным вызовом новой информации, которую мне затем нужно будет передать в пользовательский интерфейс.
Эта операция может быть отменена другим вызовом аппаратного API, который устанавливает флаг, который может видеть аппаратное обеспечение, чтобы оно могло выйти.
Я обернул этот API C своим собственным кодом C #, и внутри оболочки мне пришлось раскрутить вызов процесса запуска в другом потоке, чтобы действие не блокировало пользовательский интерфейс.

Вот отредактированные основные моменты того, что я делаю.

public class DeviceWrapper
{
    Thread childThread = null;

    void DeviceWrapper 
    {
        //Set the callback to be used by the StartGettingInformation() process
        PInvokeMethods.SetGetInformationCallback(InformationAcquiredCallback);
    }

    public void StartProcess()
    {
        childThread = new Thread(new ThreadStart(GetInformationProcess))
        childThread.Start();
    }

    void GetInformationProcess()
    {
        PInvokeMethods.StartGettingInformation();
    }

    //This callback occurs inside the childThread
    void InformationAcquiredCallback(Status status, IntPtr information)
    {
        //This callback is triggered when anything happens in the 
        //StartGettingInformation() method, such as when the information 
        //is ready to be retrieved, or when the process has been cancelled.
        if(status == Status.InformationAcquired)
        {
            FireUpdateUIEvent();
        }
        //If the cancel flag has been set to true this will be hit.
        else if(status == Status.Cancelled) 
        {
            //Reset the cancel flag so the next operation works ok
            PInvokeMethods.SetCancelFlag(false); 

            childThread.Abort();
        }
    }

    //This method runs once, and can't run at the same time as GetInformationProcess
    public string GetSpecificInformation()
    {
        //This triggers InformationAcquiredCallback with a status of Cancelled
        StopProcess(); 

        if(childThread.IsAlive)
        {
            childThread.Join();
        }

        return PInvokeMethods.GetSpecificInformation();
    }

    public void StopProcess()
    {
        PInvokeMethods.SetCancelFlag(true);
    }
}

Используя этот код, когда я вызываю childThread.Join (), все приложение останавливается (что я ожидаю для пользовательского интерфейса, и это нормально), и childThread также, кажется, останавливается, потому что обратный вызов никогда не срабатывает снова.

Однако, если я вместо этого использую следующий код:

public string GetSpecificInformation()
{
    //This triggers InformationAcquiredCallback with a status of Cancelled
    StopProcess(); 
    string s = "";

    ThreadPool.QueueUserWorkItem(new WaitCallback(delegate
    {
        if(childThread.IsAlive)
        {
            childThread.Join();
        }
        s = PInvokeMethods.GetSpecificInformation();            
    }));

    return s;
}

Затем все удаляется, как ожидалось, и childThread завершает работу, и все хорошо, за исключением того, что моя строка возвращается пустой до того, как WaitCallback сработает и присваивается ей.

Итак, мне просто нужно смириться с этим и изменить класс, чтобы я использовал QueueUserWorkItem и WaitCallback и вызвал событие для обработки моего возврата строки?
Есть ли что-то глупое, что я делаю в своем первом подходе, что также приводит к блокировке childThread?
Или есть какая-то другая тактика или класс, который я должен использовать целиком, учитывая, что у меня работает .NET 3.5?

1 Ответ

5 голосов
/ 03 апреля 2012

Ну, FireUpdateUIEvent(); звучит как метод, который может Опубликовать Отправить в MsgQueue (Control.Invoke()). Когда основной поток ожидает в Join(), тогда у вас классический тупик.

Кроме того, childThread.Abort() не считается безопасным.

Итак, мне просто нужно смириться с этим и изменить класс, чтобы я использовал QueueUserWorkItem и WaitCallback и вызвал событие для обработки моего возврата строки?

Я бы, конечно, изменил его дизайн. Это, вероятно, можно немного упростить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...