2010-12-16 3 views
4

У меня есть форма с моими настраиваемыми элементами управления.Работа с перекрестными потоками недействительна, даже если используется InvokeRequired

У меня есть метод в моей форме:

private void SetEnabledOnControls(bool val) 
{ 
    if (InvokeRequired) 
    { 
     Invoke((Action<bool>)SetEnabledOnControls, val); 
    } 
    else 
    { 
     //do the work - iterate over child controls, 
     //and they iterate over their children, etc... 
    } 
} 

А внутри методов, которые находятся на else ветви я получаю Упомянутый исключение:
Cross-thread operation not valid: Control 'txtNumber' accessed from a thread other than the thread it was created on.

Мой сценарий на самом деле немного сложнее - Я просто экстраполировал это как пример. Что на самом деле происходит, так это то, что я использую WorkflowFoundation - у меня есть StateMachineActivity (CTP1), работающий в WorkflowApplication (который работает в собственном потоке), я подписался на это событие, а оттуда я звоню SetEnabledOnControls. Кроме того, я использую закладки для возобновления рабочего процесса (а также MEF на стороне, не участвующей в сценарии).

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

Может ли кто-нибудь пролить свет на это?

Спасибо!

EDIT Используя GWLlosa предложение, я отслеживал ThreadId с помощью System.Threading.Thread.CurrentThread.ManagedThreadId. Теперь наступает странная часть ... Идентификатор потока в Initialize() равен 10. Между прохождением первых 2 состояний он входит в Id 13 - InvokeRequired был true, и он вызывался правильно. НО, после второго состояния, когда он входит в SetEnabledOnControls, это снова 13, но на этот раз InvokeRequired является ложным! Как так!? Позже, конечно, он не может изменить контроль над детьми (не удивительно). Может быть, форма как-то изменила нить, в которой она живет?

EDIT 2 Теперь я звоню с:

if (IsHandleCreated) 
{ 
    Invoke((Action<bool>)SetEnabledOnControls, val); 
} 

и имеет IsHandleCreated к истинным, но все еще не с чем devSpeed ​​pointed at.

EDIT 3 FACEPALM :) Одна из кнопок, которые были возобновления состояние было в первом CancelButton для формы. Когда он был удален из свойства как такового, у кода еще был DialogResult = Отмена для него - так что моя форма действительно закрывалась, и, конечно, у него отсутствовал дескриптор, поэтому InvokeRequired не возвращал правильную информацию и, следовательно, ошибки ,

Спасибо всем! Сегодня я узнал что-то новое :)

ответ

1

Возможно, вы используете this problem.

+0

Действительно я столкнулся с этим в конце ... полезная ссылка;) – veljkoz 2010-12-17 12:56:18

2

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

+0

Очень полезно! Посмотрите мое обновление для результатов ... – veljkoz 2010-12-17 10:48:00

0

Я просто столкнулся с подобной проблемой сам, где у меня есть функция, разрывая вниз, а затем динамически создавать элементы управления в FlowLayoutPanel:

 public static void RenderEditorInstance(DataContext dataContext, object selectedItem, Form targetForm, Control targetControl, List<DynamicUserInterface.EditorControl> editorControls, EventHandler ComboBox_SelectedIndexChanged, EventHandler TextBoxControl_TextChanged, EventHandler CheckBox_CheckChanged, EventHandler NumericUpDown_ValueChanged, CheckedListControl.ItemChecked OnItemChecked, EventHandler dateTimePicker_ValueChanged, DynamicUserInterface.DuplicationValidationFailed liveLookupValidationFailed, DynamicUserInterface.PopulateComboBoxCallback populateComboBoxCallback) 
     {   if (targetForm.InvokeRequired) 
      { 
       InstanceRenderer renderer = new InstanceRenderer(RenderEditorInstance); 
       targetForm.Invoke(renderer, dataContext, selectedItem, targetForm, targetControl, editorControls, ComboBox_SelectedIndexChanged, TextBoxControl_TextChanged, CheckBox_CheckChanged, NumericUpDown_ValueChanged, OnItemChecked, dateTimePicker_ValueChanged, liveLookupValidationFailed, populateComboBoxCallback); 
      } 
      else 
      { 
       targetControl.Padding = new Padding(2); 
       targetControl.Controls.Clear(); 

       ...{other code doing stuff here } 
      } 
     } 

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

Используя предложение, сделанное GWLlosa, я написал метод расширения для управления, чтобы получить OwningThread управления принадлежит:

public static Thread OwnerThread(this Control ctrl) 
    { 
     Thread activeThread = null; 

     if (ctrl.InvokeRequired) 
     { 
      activeThread = (Thread)ctrl.Invoke(new Func<Control, Thread>(OwnerThread), new object[] { ctrl }); 
     } 
     else 
     { 
      activeThread = Thread.CurrentThread; 
     } 

     return activeThread; 
    } 

..which отметил, что после нескольких итераций Id Темы действительно меняется.

Похоронен глубоко внутри некоторые из этого кода были использованы процедуры для извлечения данных заполнения соответствующих элементов управления, за счет использования Task.Run(), который из MSDN (https://msdn.microsoft.com/en-us/library/hh195051(v=vs.110).aspx) ясно сказано:

Пример показывает, что асинхронная задача выполняется на другом нити, чем основной поток приложения

После того, как Task.Run() был взят из уравнения, поток элемента управления не изменились. Поэтому вам нужно быть осторожным, как и когда вы его используете!

Смежные вопросы