Исключение операции между потоками, когда рабочий поток добавляет в BindingList - PullRequest
5 голосов
/ 13 августа 2010

У меня есть рабочий поток, которому нужно добавить элементы в BindingList.Однако BindingList привязан к DataGridView.Итак, когда я пытаюсь добавить в список, я получаю InvalidOperationException (Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.)

Обычно для этого исключения вы должны сделать:

if(winformControl.InvokeRequired) {
    winformControl.Invoke(MethodDelegate);
}

Однако привязка данных запутывает вещи, так как естьнет контроля Winform в поле зрения.Все, что у меня есть, это следующая строка, которая выдает исключение:

ClassInstance.MyBindingList.Add(myObject);

Если у вас есть решение специально для этого сценария, отлично.

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

Ответы [ 4 ]

1 голос
/ 13 августа 2010

Один из вариантов здесь - указать BindingList<T> использовать синхронизирующий контекст, вот так - однако, возможно, это не лучший подход. Интересно, можете ли вы представить свои данные через событие или подобное (вместо добавления непосредственно в список), а затем попросите ваш пользовательский интерфейс обработать событие, отправив в нужный поток и добавив в модель пользовательского интерфейса.

1 голос
/ 13 августа 2010

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

private System.Threading.SynchronizationContext mContext = null;

/// <summary>
/// Constructor for MyBackgroundWorkerClass
/// </summary>
public MyBackgroundWorkerClass(System.Threading.SynchronizationContext context)
{
    mContext = context;
}

Затем, когда вам нужно вызвать что-то в потоке пользовательского интерфейса:

private void CallOnTheUiThread(object dataToPassToUiThread)
{
    // Make sure the code is run on the provided thread context.
    // Make the calling thread wait for completion by calling Send, not Post.
    mContext.Send(state =>
        {
            // Change your UI here using dataToPassToUiThread.  
            // Since this class is not on a form, you probably would 
            // raise an event with the data.
        }
    ), null);
}

При создании рабочего класса изформа в потоке пользовательского интерфейса, это то, что вы бы передать в качестве контекста синхронизации.

private void Form1_Load(object sender, EventArgs e)
{
    var worker = new MyBackgroundWorkerClass(SynchronizationContext.Current);
}
0 голосов
/ 13 августа 2010

BackgroundWorkers просты в реализации, если вы можете с учетом требований.

Определите метод DoWork, который выполняется в фоновом потоке, например сохраняет в базу данных.Метод RunWorkerCompleted вызывается по завершении DoWork.RunWorkerCompleted выполняется в потоке пользовательского интерфейса, и вы можете без проблем обновить список представлений.

// on the UI thread
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += DoWork;
worker.RunWorkerCompleted += RunWorkerCompleted;
worker.RunWorkerAsync("argument");

События:

static void DoWork(object sender, DoWorkEventArgs e)
{
    e.Result = "4";
}

static void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error == null)
    {
        string a = (string)e.Result;
        Console.WriteLine(a);
    }
    else
    {
        Console.WriteLine(e.Error.Message);
    }
}
0 голосов
/ 13 августа 2010

Вы можете инициировать событие в главном, пользовательском интерфейсе, потоке, и там есть:

if (this.InvokeRequired)
{
    this.Invoke(...);
}

, поэтому вы тестируете само главное окно.

...