Любое решение для исключения незаконной операции перекрестной резьбы?

Когда вы связываете данные в C#, поток, который изменяет данные, также вызывает изменение элемента управления. Но если этот поток не тот, на котором был создан элемент управления, вы получите исключение Illegal Cross Thread Operation.

Есть ли способ предотвратить это?

4 ответа

Решение

Если изменение данных не занимает слишком много времени (то есть, если основная цель фонового потока не заключается в действительной модификации данных), попробуйте переместить секцию, которая изменяет данные, в делегат и вызвать этот делегат.

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

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

Вы должны быть в состоянии сделать что-то вроде:

if (control.InvokeRequired)
{
    control.Invoke(delegateWithMyCode);
}
else
{
    delegateWithMyCode();
}

InvokeRequired - это свойство в Controls, позволяющее определить, находитесь ли вы в правильном потоке, тогда Invoke вызовет делегата в правильном потоке.

ОБНОВЛЕНИЕ: На самом деле, на моей последней работе мы сделали что-то вроде этого:

private void SomeEventHandler(Object someParam)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new SomeEventHandlerDelegate(SomeEventHandler), someParam);
    }

    // Regular handling code
}

что устраняет необходимость в блоке else и как бы ужесточает код.

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

public delegate void DataBindDelegate();
public DataBindDelegate BindData = new DataBindDelegate(DoDataBind);

public void DoDataBind()
{
    DataBind();
}

Если привязка данных должна выполняться конкретным потоком, то пусть этот поток сделает всю работу!

В WPF и Silverlight инфраструктура привязки обеспечивает переключение на поток пользовательского интерфейса.

Если вызов потока "недопустим" (т. Е. Вызов DataBind влияет на элементы управления, которые не были созданы в потоке, из которого он вызывается), вам необходимо создать делегат, чтобы даже если решение / подготовка для DataBind не была выполнена в поток создания элемента управления, любая результирующая их модификация (например, DataBind()).

Вы бы назвали мой код из рабочего потока следующим образом:

this.BindData.Invoke();

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

Другие вопросы по тегам