Как получить доступ к объекту из другого потока без конфликта?

#c# #multithreading #thread-safety

#c# #многопоточность #безопасность потоков

Вопрос:

У меня есть поток с именем fetchImage, который запускает метод startFetch(), который

      for (int i = 0; !stopFetching amp;amp; i < 2000; i  ) {
      control.fetchImage().Save("Fetch_Image\fetchedFile"   fileName   ".JPG", System.Drawing.Imaging.ImageFormat.Jpeg);
            fileName  ;
        //Thread.Sleep(800);
            Console.WriteLine("Filename :"   fileName);
  

control — это еще один класс, который имеет метод fetchImage():

  return (System.Drawing.Bitmap)mainForm.imageTemp.Clone();
  

mainForm это форма, которая имеет переменную изображения с именем imageTemp. Теперь startFetch() работает нормально, когда у меня есть Thread.Sleep(800) , но это выдает мне эту ошибку без сна. Вот трассировка стека ошибки

 System.InvalidOperationException was unhandled
  Message=Object is currently in use elsewhere.
  Source=System.Drawing
  StackTrace:
       at System.Drawing.Image.Clone()
       at KiPNi.Control.fetchImage() in F:CMPS285285-fall10-5Final working codeWindowsFormsApplication7Control.cs:line 65
       at KiPNi.Form4.startFetch() in F:CMPS285285-fall10-5Final working codeWindowsFormsApplication7Form4.cs:line 80
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 
  

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

Как мне получить доступ к изображению из потока fetchImage?

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

1. Вам действительно нужно опубликовать код того, что fetchImage делает. Ресурс (он же файл) блокируется другим потоком.

2. Растровое изображение может использоваться только одним потоком одновременно. Мы видим, что ваш рабочий поток обращается к нему, это код, который взорвался. Он также используется в другом потоке. Вероятно, это основной поток, пытающийся нарисовать растровое изображение. Я бы предположил, что вы получили его в PictureBox. Для этого нет реального решения, кроме как не показывать его в PB или создать свой собственный элемент управления, чтобы вы могли использовать ключевое слово lock вокруг графики. Вызов drawImage() .

3. @SilverNinja, извините, я неправильно указал: fetchImage() просто возвращает клон изображения imageTemp из MainForm. return (System.Drawing.Bitmap)mainForm.imageTemp.Clone();

4. @HansPassant Я понимаю концепцию, о которой вы говорите. Я думаю, что это применимо и к другим компонентам GUI (вызывающий поток не может изменять значения экземпляров других потоков). Я не использую Graphics.DrawImage() , но System. Рисование. Объект изображения (Visual C #).

5. Чего я не могу добиться, так это того, что он работает нормально, если я это сделаю Thread.sleep() . Однако, если я это сделаю, изображения, которые я отправляю на сервер в секунду, будут меньше по сравнению с тем, когда я выполняю цикл без прерывания. И вы правы насчет imageTemp, imageTemp = pictureBox1.Image; где pictureBox1 ссылается на видео с веб-камеры. @Hans Passant

Ответ №1:

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

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

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

1. Я понимаю, что вы говорите. Причина, по которой я создал Clone() imageTemp, заключалась в том, чтобы избежать такого конфликта return (System.Drawing.Bitmap)mainForm.imageTemp.Clone(); . Прямо сейчас я реализовал a Forms.Timer() , который тикает каждые 1/2 секунды. Пока ошибки нет. Но я действительно хочу получать больше кадров в секунду (по возможности непрерывно). Я был бы очень признателен за код, демонстрирующий то, что вы говорите. Спасибо за вашу помощь

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