InvalidOperationException - объект в настоящее время используется в другом месте - PullRequest
24 голосов
/ 05 декабря 2009

Я прошел этот ТАК вопрос , но это не помогло.

Дело здесь в другом. Я использую 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
}

аналогичные операции выполняются в другом алгоритме * backgrougrondworker_doWork ().

Теперь ИНОГДА я получаю "InvalidOperationException - объект в настоящее время используется в другом месте". Это очень произвольно. Иногда я получаю это в algo1backgroundworker_DoWork, иногда в algo2backgroundworker_DoWork, а иногда в Application.Run (новый myWindowsForm ());

Я понятия не имею, что происходит.

Ответы [ 3 ]

46 голосов
/ 05 декабря 2009

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

Вам нужно ограничить доступ к растровому изображению только одним потоком. Клонируйте изображения в потоке пользовательского интерфейса перед запуском BGW, каждому BGW требуется своя копия изображения. Обновите свойство изображения PB в событии RunWorkerCompleted. Таким образом, вы потеряете некоторое количество параллелизма, но это неизбежно.

22 голосов
/ 05 декабря 2009

Похоже, что ваши 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{}, другие, которые приходят к этому моменту, будут заблокированы. Поэтому важно убедиться, что код в заблокированном разделе быстрый.

Когда вы начинаете «выводить» обработанное изображение, будьте осторожны, чтобы не допустить сквозного обновления пользовательского интерфейса. Проверьте этот пост для аккуратного способа избежать этого.

1 голос
/ 05 декабря 2009

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

Это означает, что в DoWork вы не должны получать доступ к каким-либо элементам управления (без использования Control.Invoke). Так что здесь вы бы назвали RunWorkerAsync, передавая клон вашего изображения. Внутри обработчика событий DoWork вы можете извлечь параметр из DoWorkEventArgs.Argument.

Только GUID обработчиков событий ProgressChanged и RunWorkerCompleted должны взаимодействовать с графическим интерфейсом.

...