обработчик событий в форме, вызываемый из потока на c#

#c# #winforms

#c# #winforms

Вопрос:

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

 public delegate void AppStateChangedEventHandler(int st);
    public event AppStateChangedEventHandler AppStateChanged;
    private int appState;
    public int AppState
    {
        get { return this.appState; }
        set
        {
            this.appState = value;
            if (this.AppStateChanged != null)
            {
                this.AppStateChanged(value);
            }
        }
    }
    public void MainForm_AppStateChanged(int val)
    {
        if (val == 1)
        {                
            totDwn.Text = "00:00:00";
            totAct.Text = "00:00:00";                
        }
        else if (val == 0)
        {                
            tt.Reset();
            sw.Reset();
        }
    }
  

Внутри метода initializecomponent я добавил

 this.AppStateChanged  = new AppStateChangedEventHandler(this.MainForm_AppStateChanged);
  

Внутри потока я изменил свойство appstate следующим образом

 this.AppState = 1;
  

Теперь есть две проблемы

1) Первое и важное — vs жалуется на то, что выполняется межпоточная операция, даже если я обращаюсь к элементам GUI из обработчика событий.(я знаю, как справиться с этим, используя invoke required и делегат, но мне просто нужно знать, почему это так)

2) VS продолжает предупреждать, что событие AppStateChanged не является частью Windows.form.

Первая проблема заключается в том, что меня действительно раздражает? Есть идеи по этому поводу.

Комментарии:

1. Используйте invoke

Ответ №1:

Редактировать: О, я неправильно понял ваш вопрос. Вы хотите знать, почему VS жалуется? Это было дизайнерское решение команды разработчиков языка C #, чтобы избежать множества проблем, связанных с межпоточным доступом к элементам управления формы.

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


Событие выполняется в потоке, который его запускает (ваш другой поток), а не в том, который его регистрирует (основной поток / GUI поток).

Чтобы решить эту проблему, вы должны убедиться, что код события вызывается в правильном потоке, чтобы разрешить доступ к элементам GUI, что может быть сделано следующим образом:

 public void MainForm_AppStateChanged(int val)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new Action<int>(MainForm_AppStateChanged), val);
    }
    else
    {
        if (val == 1)
        {                
            totDwn.Text = "00:00:00";
            totAct.Text = "00:00:00";                
        }
        else if (val == 0)
        {                
            tt.Reset();
            sw.Reset();
        }
    }
}
  

Комментарии:

1. «, но лучше прерваться последовательным и легко исправляемым способом, чем потерпеть неудачу странными способами, которые кажутся случайными из-за того, что два потока манипулируют не-потокобезопасным управлением» я этого не понимаю @SirViver, можете ли вы объяснить это. Вы хотите сказать, что создавать пользовательский обработчик событий нехорошо.

2. @swordfish: Нет, это не имеет ничего общего с пользовательскими обработчиками событий. Любой код, который пытается изменить элементы управления, созданные в другом потоке, вызывает эту ошибку, что хорошо, потому что при попытке получить к нему доступ происходит немедленный сбой и очень предсказуемым образом. Разрешение доступа к управлению между потоками (без вызова или специальных усилий по синхронизации), возможно, будет работать в 95% случаев, а затем незаметно (или не очень незаметно) полностью отключится случайным образом. С текущей реализацией разработчик немедленно вынужден делать это правильно, что избавляет его от большой головной боли в долгосрочной перспективе.

Ответ №2:

Основная причина этой проблемы заключается в том, что вы пытаетесь получить доступ к элементам управления, таким как totDwn amp; totAct.Текст в другом потоке, затем поток создал его. Итак, вы можете попробовать следующий код, чтобы решить эту проблему. Попробуйте сохранить SynchronizationContext экземпляр в основном приложении. Я предпочитаю OnLoad метод

 SynchronizationContext Context = SynchronizationContext.Current;
  

После этого в вашем методе обработчика событий вы могли бы сделать

 public void MainForm_AppStateChanged(int val)
{
    Context.Post((a)=> {

        if (val == 1)
        {                
            totDwn.Text = "00:00:00";
            totAct.Text = "00:00:00";                
        }
        else if (val == 0)
        {                
            tt.Reset();
            sw.Reset();
        }
    },null);
}
  

Ответ №3:

обработчики событий выполняются в потоке, который их вызвал, чтобы сохранить переключение контекста, вот почему вы получаете исключение.