2008-11-05 3 views
21

UI 스레드에서 호출을 호출하기 위해 디스패처를 사용하려고 할 때 '걸려있는'것처럼 보이거나 대기 호출에서 멈추는 다소 복잡한 WPF 응용 프로그램이 있습니다.WPF Dispatcher.Invoke 'hanging'

일반적인 절차는 다음

  1. 새로운 스레드 (STA) 만들기 버튼 클릭 이벤트를 처리 : 발표자 및 UI의 새로운 인스턴스를 생성을, 방법 끊기 호출
  2. :
  3. 분리 다음 UI의 속성이 이름을
  4. 이름에 대한 세터 다음 속성을 설정하려면 다음 코드를 사용합니다라는 설정

    if(this.Dispatcher.Thread != Thread.CurrentThread) 
    { 
     this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{ 
      this.Name = value; // Call same setter, but on the UI thread 
     }); 
     return; 
    } 

    SetValue(nameProperty, value); // I have also tried a member variable and setting the textbox.text property directly. 

내 문제는 발송자가 메서드를 호출 호출 할 때마다 한 시간을 걸어 것 같다, 그리고 호출 스택은 수면에, 기다리거나 호출 구현 이내에 가입을 나타냅니다.

그래서 내가 놓친 부분이 분명하지 않습니까? 아니면이 속성 (및 기타)을 설정하기 위해 UI 스레드를 호출하는 더 좋은 방법이 있습니까?

편집 : 솔루션 (예를 들어 작업이 수행되고 있던 곳) 스레드 대표의 끝에서() System.Windows.Threading.Dispatcher.Run 전화를했다 - 도와 준 모두에게 감사합니다.

+0

@Matthew - 사실, BeginInvoke에 대해서는 "최적이 아닌"것이 없습니다. 당신이 절대적으로 업데이트 *를 필요로하지 않는다면, 괜찮습니다. 하지만 캡쳐 된 변수에 대해서는 약간주의해야합니다 (BeginInvoke를 호출 한 후 "value"를 변경하지 마십시오.) –

+0

@Matthew - 새 스레드를 조인 (join)하지 않습니까? 그걸 설명 할거야 ... –

+1

@Marc Gravell - 메모리에서 나는 어떤 시점에서 스레드에 합류하고 있었지만, 그 동작을 사용하지 않을 때 같은 동작인지 잘 모르겠습니다. 참여의 이유는 작업이 완료 될 때까지 나머지 응용 프로그램을 차단하고 싶었지만 어쩌면 대안을 사용할 수 있습니다. –

답변

5

새 STA 스레드를 만들고 있는데,이 새 스레드의 디스패처가 실행 중입니까?

"this.Dispatcher.Thread! = Thread.CurrentThread"에서 다른 발송자가 될 것으로 예상됩니다. 그렇지 않으면 실행 대기열을 처리하지 않는지 확인하십시오.

+0

키이스, 이것은 좋은 지적이다. 나는 배차자와 충분히 친숙하지는 않지만 창 디스패처가 이미 시작되지 않았습니까? STA 스레드는 새 창을 만드는 데 사용되지만 수동으로 발송자를 시작해야하는 이유는 처리하지 않는 이유를 설명하는 것입니다. –

+0

STA를 직접 만드는 경우 창을 표시 한 후 Dispatcher.Run()을 호출 해보십시오. . 디스패처는 메시지 펌프이며, 새로운 UI 스레드를 만들면 요청할 때 디스패처를 만들게됩니다. 생성을 관리하는 경우 디스패처에서 실행을 호출해야합니다. – Keith

+1

이 게시물을보십시오 : http://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/ – Keith

10

Invoke가 동기식입니다. Dispatcher.BeginInvoke가 필요합니다. 또한, 귀하의 코드 샘플을 "다른"문 안에 "SetValue"이동해야합니다 믿습니다.

2

이것은 교착 상태처럼 들립니다. 이것은 .Invoke를 호출하는 스레드가 UI 스레드가 작업을 완료하는 데 필요한 lock/mutex/etc를 이미 보유하고있는 경우에 일반적으로 발생합니다. 가장 간단한 방법은 BeginInvoke를 대신 사용하는 것입니다. 즉, 현재 스레드가 계속 실행될 수 있으며 곧 잠금을 잠시 ​​해제 (UI가 해당 키를 획득 할 수 있도록)합니다. 또는 문제가되는 잠금을 식별 할 수 있으면 의도적으로 잠금을 해제 할 수 있습니다.

+0

Marc에게 감사드립니다.이 설명은 훌륭하지만, 처음에는 왜 자물쇠가 있는지에 관해서는 여전히 우둔한 채로 남아 있습니다.자신과 Paul BeginInvoke가 제안했지만 최적이 아니기 때문에 완료 할 수는 없습니다. 미친 이상한 벌레 .... –

1

나는 비슷한 문제에 봉착하고 난 여전히 대답이 무엇인지 확실하지 않다 동안, 내가 생각하는 당신의

if(this.Dispatcher.Thread != Thread.CurrentThread) 
{ 
    this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{ 
     this.Name = value; // Call same setter, but on the UI thread 
    }); 
    return; 
} 

는 표시되지 않습니다

if(this.Dispatcher.CheckAccess()) 
{ 
    this.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate{ 
     this.Name = value; // Call same setter, but on the UI thread 
    }); 
    return; 
} 

의 checkAccess 교체해야합니다 Intellisense에서 작동하지만이 목적을위한 의미가 있습니다. 또한, 일반적으로 BeginInvoke를 원한다는 것에 동의하지만,이 비동기를 수행 할 때 UI 업데이트를 얻지 못한다는 사실을 발견했습니다. 불행히도, 내가 동 기적으로 할 때 교착 상태가 발생합니다 ...

3

나는 (! this.Dispatcher.나는 이것이 알고

0

:-(책에 의해 모든 일을 할 것 같다 -의 checkAccess은())

나는 또한이 호출에 응답하지, 또는 내가 BeginInvoke 할 수있는 내 대리인이 호출되고 있지 않은 경우에 geting하고 여기에 기존 스레드,하지만 다른 솔루션 난 그냥 비슷한 문제를 해결

내 발송자

나는 DEBUG 표시했다 ... 잘 실행 해했다 -..> THREAD 창 모든 스레드를 식별을 그 내 코드를 어디서나 실행하고 있습니다.

c 각 스레드를 들으며, 어떤 스레드가 교착 상태를 일으켰는지 빠르게 알았다.

lock (locker) { ... } 문과 Dispatcher.Invoke() 호출을 결합하는 여러 스레드였습니다.

내 경우에는 특정 lock (locker) { ... } 문을 변경하고 Interlocked.Increment(ref lockCounter)으로 바꿀 수 있습니다.

교착 상태가 발생하지 않았으므로 문제가 해결되었습니다.

void SynchronizedMethodExample() { 

    /* synchronize access to this method */ 
    if (Interlocked.Increment(ref _lockCounter) != 1) { return; } 

    try { 
    ... 
    } 
    finally { 
     _mandatoryCounter--; 
    } 
} 
7

이 코드가 더 잘 표시되어 있습니다.

someDispatcher.Invoke(() => 
{ 
    lock (someObject) 
    { 
     // Do something. 
    } 
} 

모든 것이 첫눈에 벌금과 멋쟁이 나타날 수 있지만하지 :

lock (someObject) 
{ 
    // Do one thing. 
    someDispatcher.Invoke(() => 
    { 
     // Do something else. 
    } 
} 

스레드 B가이 작업을 수행 :

스레드 A는이 수행이 시나리오를 생각해 보자. 교착 상태가 발생합니다. Dispatcher는 스레드의 대기열과 같으며 이러한 교착 상태를 처리 할 때 "이전의 파견이 내 대기열을 막을 수 있었던 것은 무엇입니까?"라고 생각하는 것이 중요합니다. 쓰레드 A가 들어가서 자물쇠 아래로 파견됩니다. 하지만 스레드 A가 "Do one thing"이라고 표시된 코드에있는 시점에 스레드 B가 들어오는 경우 어떻게해야합니까? 음 ...

  • 스레드 A는 someObject에 대한 잠금을 가지고 있으며 일부 코드를 실행 중입니다.
  • 스레드 B가 디스패치되고 디스패처가 someObject에 대한 잠금을 얻으려고 시도합니다. 스레드 A에 스레드가 이미 있으므로 디스패처를 방해합니다.
  • 그러면 스레드 A가 다른 발송 항목을 대기열에 추가합니다. 운영자가 이전 요청 처리를 완료하지 않기 때문에이 항목은 해고되지 않습니다. 그것의 이미 위로 묶여.

이제 교착 상태가 종료되었습니다.

+0

좋은 설명 주셔서 감사합니다. 근무 시간을 절약 해주었습니다. Dispatcher 호출 (스레드 B가하는 일)에서 잠금을 획득하지 않음으로써 해결했습니다. 이 문제에 대한 또 다른 해결책이 있습니까? – Heribert

+0

@Heribert 작업하는 코드에 따라 다릅니다. 이러한 교착 상태는 응용 프로그램별로 다릅니다. 위와 유사한 사례가 발생하면 Dispatcher 호출 외부에 잠글 수 있습니다. – Alexandru

+0

그게 내가 한 짓이야 :) 다시 게시물에 대한 감사와 회신 – Heribert