InvalidOperationException - объект в настоящее время используется в другом месте
Я прошел этот вопрос SO, но это не помогло.
Случай здесь другой. Я использую Backgroundworkers. 1-й фоновой рабочий начинает работать на входе пользователя и внутри firstbackgroundworker_runworkercompleted() Я использую вызов 3 других фоновых работ
algo1backgroundworker.RunWorkerAsync();
algo2backgroundworker.RunWorkerAsync();
algo3backgroundworker.RunWorkerAsync();
это код для каждого:
algo1backgroundworker_DoWork()
{
Image img = this.picturebox.Image;
imgclone = img.clone();
//operate on imgclone and output it
}
algo2backgroundworker_DoWork()
{
Image img = this.picturebox.Image;
imgclone = img.clone();
//operate on imgclone and output it
}
аналогичные операции выполняются в другом algo * backgrougrondworker_doWork().
Теперь ИТОГО Я получаю объект InvalidOperationException, который в настоящее время используется в другом месте. Это очень произвольно. Я иногда получаю это в algo1backgroundworker_DoWork, а иногда и в algo2backgroundworker_DoWork, а иногда и в Application.Run(new myWindowsForm());
Я не знаю, что происходит.
Ответы
Ответ 1
Внутри GDI + есть блокировка, которая предотвращает одновременное обращение двух потоков к растровому изображению. Это не блокирующий вид блокировки, это "программист сделал что-то не так, я выброшу исключение" вроде блокировки. Ваши потоки бомбардируются, потому что вы клонируете изображение (== доступ к растровому изображению) во всех потоках. Ваш поток пользовательского интерфейса бомбит, потому что он пытается рисовать растровое изображение (== доступ к растровому изображению), в то время как поток клонирует его.
Вам нужно ограничить доступ к растровому изображению только одним потоком. Клонирование изображений в потоке пользовательского интерфейса, прежде чем запускать BGW, каждый BGW нуждается в собственной копии изображения. Обновите свойство PB Image в событии RunWorkerCompleted. Вы потеряете concurrency таким образом, но это неизбежно.
Ответ 2
Итак, похоже, что ваши BackgroundWorkers пытаются одновременно получить доступ к тем же компонентам Windows Forms. Это объясняет, почему неудача является случайной.
Вам нужно убедиться, что этого не происходит, используя lock
, возможно, так:
private object lockObject = new object();
algo1backgroundworker_DoWork()
{
Image imgclone;
lock (lockObject)
{
Image img = this.picturebox.Image;
imgclone = img.clone();
}
//operate on imgclone and output it
}
Обратите внимание, что я уверен, что imgclone является локальным для этого метода - вы определенно не хотите делиться им по всем методам!
С другой стороны, тот же экземпляр lockObject используется всеми методами. Когда метод BackgroundWorker входит в раздел lock{}
, другие, которые приходят в эту точку, будут заблокированы. Поэтому важно убедиться, что код в заблокированном разделе выполняется быстро.
Когда вы приходите на "вывод" обработанного изображения, будьте осторожны, чтобы убедиться, что вы не выполняете кросс-потоковое обновление для пользовательского интерфейса. Проверьте этот пост, чтобы избежать этого.
Ответ 3
В окнах не только вы должны получать доступ только к элементам управления из одного потока, но этот поток должен быть основным потоком приложения, потоком, который создал элемент управления.
Это означает, что в DoWork вы не должны обращаться к каким-либо элементам управления (без использования Control.Invoke). Таким образом, вы бы назвали RunWorkerAsync, проходящий в вашем клоне изображения. Внутри обработчика событий DoWork вы можете извлечь параметр из DoWorkEventArgs.Argument.
Только обработчики событий ProgressChanged и RunWorkerCompleted должны взаимодействовать с графическим интерфейсом.