2012-05-15 2 views
1

Я создаю приложение, использующее TPL в VS2010 Ultimate. В большинстве случаев, когда я запускаю приложение, он становится невосприимчивым, когда я вызываю DoRepresentation() из потока пользовательского интерфейса.Тупик с использованием Control.Invoke?

void DoRepresentation() 
{ 
    Parallel.ForEach(cgs, loopOptions, g => 
    { 
    UpdateRepresentation(g); 
    }); 
} 

void UpdateRepresentation(object g) 
{ 
    view.Invoke(new Action(() => 
    { 
    representation = new MyRepresentation(g); 
    })); 
} 

Я не знаю, почему приложение становится невосприимчивым. Есть ли у меня тупик?

Внутри MyRepresentation Я звоню в OpenGL.

вид - это элемент управления внутри формы1 (основная форма).

Когда приложение перестает отвечать на запросы приостановить его с VS IDE и вот информацию я получить

В «Параллельные задачи» окно я получаю следующее:

ID Status  Message<br> 
1 ?Waiting Task1 is waiting on object: "Task2"<br> 
2 ?Waiting No waiting information available<br> 

В окно "Call Stack" я получаю следующее:

[In a Sleep, wait, or join]<br> 
[External Code]<br> 
Test.dll!Render.DoRepresentation()<br> 
App1.exe!Form1.Button1_Click<br> 

Любая помощь будет apprec iated.

+0

Не можете ли вы использовать 'BeginInvoke', а не' Invoke'? – Nick

+0

Нет, потому что мне нужно блокировать до возврата UpdateRepresentation(). Потому что после Parallel.ForEach() я делаю некоторые другие вызовы для некоторых методов. – Michelle

+0

Может быть, глупый вопрос, но у View есть Диспетчер, чтобы вы могли делать view.Dispatcher.Invoke()? – Davio

ответ

7

Да, вы с мертвой точки. Что делает Parallel.ForEach(), так это то, что он запускает итерации, используя один или несколько потоков , включая текущий, а затем блокирует текущий поток до тех пор, пока все итерации не будут завершены.

Это означает, что если вы звоните DoRepresentation() из потока пользовательского интерфейса, вы получаете в тупике: поток UI ждет итераций на других потоках до конца, в то время как те, другие потоки ждут Invoke() до конца, что не может произойти если поток пользовательского интерфейса заблокирован.

Кроме того, в вашем случае использование Parallel.ForEach() не имеет смысла (при условии, что это ваш фактический код): вы запускаете new MyRepresentation() в потоке пользовательского интерфейса.

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

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

+0

, когда вы говорите в 1-ом абзаце «включая текущий», вы говорите о потоке пользовательского интерфейса (основного)? Я должен выполнить MyRepresentation() в потоке пользовательского интерфейса, потому что я делаю некоторые вызовы API OpenGL и контекст создается в потоке пользовательского интерфейса – Michelle

+1

Да, если вы запустите 'ForEach()' из потока пользовательского интерфейса, то некоторые его итерации будут выполняться в потоке пользовательского интерфейса. И если вам нужно запустить 'new MyRepresentation()' в потоке пользовательского интерфейса, то почему вы даже пытаетесь использовать 'Parallel.ForEach()'? В коде нет ничего, что * может * быть распараллелировано. – svick

+0

@sivck спасибо за вашу помощь Теперь я на 100% поняла, что происходит. Вы правы в выполнении MyRepresentation(), используя Parallel.ForEach(), но я должен выполнить некоторые интенсивные вычисления внутри MyRepresentation и очень немного строк внутри Invoke для выполнения вызовов API OpenGL. Но я не показал его здесь для простоты – Michelle

0

Вы можете использовать метод BeginInvoke для вызова метода Invoke. если вам все еще нужно, вы можете заблокировать объект и убедиться, что он не будет доступен из другого потока до его реализации.

с помощью Begin Invoke метод

void UpdateRepresentation(object g) 
{ 
    view.BeginInvoke(new Action(() => 
    { 
    representation = new MyRepresentation(g); 
    })); 
} 

Использование блокировки

void UpdateRepresentation(object g) 
{ 
lock(this) 
{ 
view.Invoke(new Action(() => 
    { 
    representation = new MyRepresentation(g); 
    })); 
} 

} 
+0

Итак, Control.Invoke() может создать тупик? – Michelle

+2

Да, 'Control.Invoke' может создать тупик, если элемент управления, который вы вызываете, ожидает завершения потока, который вызывает' Control.Invoke'. Существуют условия, когда 'Control.Invoke' может создавать взаимоблокировки - это ваша задача решить эту проблему :-) –

+0

@ThorstenDittmar благодарит вас за объяснение. Но это не так, потому что управление представлением является сторонним. – Michelle

0

Этот комментарий относится к моему конкретному приложению, которое является приложением Windows на C#: использование Lock также не помогло мне, и приложение просто замерзло. BeginInvoke работал, но мне не понравился эффект асинхронного обновления элементов управления пользовательского интерфейса.

Я закончил основной процесс как отдельный поток (System.Threading.Tasks.Task), который начнется и мгновенно даст мне контроль над основным потоком. Впоследствии, ожидая еще нескольких задач для завершения выполнения в цикле, мне также пришлось вставить эту строку: System.Windows.Forms.Application.DoEvents(), чтобы позволить системе обрабатывать все сообщения, ожидающие очереди. Теперь он подходит для моего приложения. Возможно, будет другой способ обмануть этого кота, но он работает сейчас.

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