2009-03-02 5 views
10

저는 현재 백그라운드 스레드에서 오는 winforms 데이터 바인딩 및 업데이트를 많이 사용하는 응용 프로그램의 데이터 바인딩 부분을 설계/재 작업하고 있습니다 (한 번에 100 개가 넘는 레코드).WinForms 멀티 스레드 데이터 바인딩 시나리오, 모범 사례?

응용 프로그램이 주식 거래 응용 프로그램이라고 가정 해 보겠습니다. 백그라운드 스레드가 데이터 변경 사항을 모니터링하고이를 데이터 개체에 저장합니다. 이러한 개체는 BindingList<>에 저장되고 INotifyPropertyChanged을 구현하여 데이터 바인딩을 통해 변경 내용을 winforms 컨트롤에 전파합니다. 또한 데이터 개체는 현재 WinformsSynchronizationContext.Send을 통해 변경 내용을 UI 스레드에 마샬링하고 있습니다. 사용자가 UI에 일부 값을 입력 할 수 있습니다. 즉, 일부 값을 양쪽에서 변경할 수 있습니다. 사용자 값은 업데이트로 인해 겹쳐서는 안됩니다.

그래서 몇 가지 질문이 내 마음에오고있다 :

  • (바인딩에 배경 업데이트) 그렇게하는 방법을 일반적인 디자인 guildline이 있습니까?
  • UI 스레드에서 마샬링하는시기 및 방법은 무엇입니까?
  • 바인딩/데이터 개체와 상호 작용하는 배경 스레드의 가장 좋은 방법은 무엇입니까?
  • 어떤 클래스/인터페이스를 사용해야합니까? (BindingSource에, ...)
  • ...

UI를 정말 컨트롤을 업데이트하는 백그라운드 스레드가 발생한 것을 알고하지 않는 나의 이해의 같은 데이터 바인딩 시나리오는 UI shouldn에 ' 데이터가 어디에서 오는지 알 수 있습니다 ... 배경 스레드가 UI에 데이터를 푸시하는 것으로 생각할 수 있습니다. 따라서 백그라운드 워커가 내가 찾는 옵션인지 확실하지 않습니다.

경우에 따라 데이터/비즈니스 개체에서 작업하는 동안 (예 : 다시 계산 중 배경 설정) UI 응답을 받고 싶을 때가 있습니다. 배경에 바인딩 된 상태 속성에 대한 속성 변경은 충분하지 않습니다. 계산이 완료된 후 컨트롤이 다시 그려지기 때문입니다. 내 생각은 propertychanged 이벤트에 연결하고 .update() 컨트롤을 호출하는 것입니다 ... 그것에 대해 다른 아이디어?

답변

6

대부분의 "솔루션"으로 인해 많은 사용자 지정 코드가 생성되고 BeginInvoke() 또는 System.ComponentModel.BackgroundWorker (그 자체는 BeginInvoke 이상의 얇은 래퍼입니다).

과거에는 데이터가 안정 될 때까지 곧 INotifyPropertyChanged 이벤트를 보내는 것을 지연하고 싶다는 사실을 발견했습니다. 하나의 적절하게 변경된 이벤트를 처리하는 코드는 종종 다른 소유물을 읽어야합니다. 또한 많은 속성 중 하나의 상태가 변경 될 때마다 자체적으로 다시 그려야하는 컨트롤이 있고 컨트롤을 너무 자주 다시 그리지 않게됩니다.

첫 번째로 각 사용자 지정 WinForms 컨트롤은 PropertyChanged 이벤트 처리기에서 자체 페인트해야하는 모든 데이터를 읽어야하므로 WM_PAINT (OnPaint) 메시지 일 때 데이터 개체를 잠글 필요가 없습니다. 새 데이터를 가져올 때 컨트롤이 즉시 다시 칠해서는 안됩니다. 대신 Control.Invalidate()이라고해야합니다. Windows는 WM_PAINT 메시지를 가능한 한 적은 수의 요청으로 결합하고 UI 스레드가 할일이 없을 때만 메시지를 보냅니다. 이렇게하면 다시 그리기 횟수와 데이터 개체가 잠기는 시간이 최소화됩니다. (표준 컨트롤은 대개 데이터 바인딩과 함께이 작업을 수행함)

데이터 개체는 변경 사항을 기록한 다음 변경 사항 집합이 완료되면 UI 스레드를 SendChangeEvents 메서드를 호출 한 다음 변경된 모든 속성에 대해 UI 스레드의 PropertyChanged 이벤트 처리기를 호출합니다. SendChangeEvents() 메서드가 실행되는 동안 백그라운드 스레드가 스레드를 업데이트하지 못하도록 데이터 개체를 잠 가야합니다.

업데이트 세트가 데이터베이스에서 콩 읽기를 가질 때마다 UI 스레드는 BeginInvoke을 호출하여 "실행"될 수 있습니다. UI 메시지 대기열이 비어있을 때 Windows에서 WM_TIMER 메시지 만 전송하기 때문에 타이머를 사용하여 UI 스레드 폴링을 수행하는 것이 더 좋으며 이로 인해 UI 응답 성이 향상됩니다.

또한 데이터 바인딩을 전혀 사용하지 않고 UI가 타이머가 작동 할 때마다 "변경된 내용"을 묻도록하십시오. 데이터 바인딩은 항상 멋지게 보이지만 솔루션의 일부가 아닌 문제의 일부가 될 수 있습니다.

데이터 개체의 잠그기/잠금 해제가 쉽지 않으므로 데이터베이스에서 업데이트를 충분히 빨리 읽을 수 없으므로 UI ​​스레드에 데이터 개체의 (가상) 복사본을 전달할 수 있습니다. 데이터 객체를 변경/변경 불가능하게 유지하면 데이터 객체를 변경하면 현재 데이터 객체를 변경하는 대신 새 데이터 객체를 반환하므로이를 활성화 할 수 있습니다.

영구 객체의 사운드가 매우 느리지 만 필요하지는 않지만 일부 포인터의 경우 thisthat을 참조하십시오. 스택 오버플로에서 thisthat도 확인하십시오.

retlang - Message-based concurrency in .NET도 확인하십시오. 메시지 일괄 처리가 유용 할 수 있습니다.

(WPF의 경우, 백그라운드 스레드에 의해 다중 스레드 모델에서 '배치'로 업데이트 된 UI 스레드를 설정하는 View-Model이 있습니다. 그러나 WPF는 데이터를 결합하는 것이 훨씬 낫습니다. 바인딩 이벤트를 누른 다음 WinForms.)

2

해당 항목에 특정 된 MSDN article이 있습니다. 그러나 VB.NET을 볼 준비를하십시오. ;)

또한 제 2의 일반적인 스레드 대신 System.ComponentModel.BackgroundWorker을 사용할 수도 있습니다. 설명하는 스폰 된 백그라운드 스레드와의 상호 작용을 정형화했기 때문일 수 있습니다. MSDN 라이브러리에있는 예제는 꽤 괜찮습니다. 사용법에 대한 힌트를 찾으십시오.

편집 : 참고 : ProgressChanged 이벤트를 사용하여 UI 스레드로 다시 통신하려면 마샬링이 필요하지 않습니다. 백그라운드 스레드는 UI와 통신 할 필요가있을 때마다 ReportProgress를 호출합니다. 이 이벤트에 어떤 객체도 첨부 할 수 있기 때문에 수동 마샬링을 할 이유가 없습니다. 진행 상황은 다른 비동기 작업을 통해 전달됩니다. 따라서 UI가 진행 이벤트를 처리하는 속도 나 백그라운드 스레드가 이벤트가 끝날 때까지 대기하여 인터럽트가 발생하지 않도록 걱정할 필요가 없습니다.

백그라운드 스레드가 진행률 변경 이벤트를 너무 빠르게 발생시키는 것을 증명하면 Pull vs. Push models for UI updates을 Ayende의 훌륭한 기사로보고 싶을 수 있습니다.

+1

System.ComponentModel.BackgroundWorker 그러나 어려운 문제는 데이터가 UI 스레드를 보지 않고 빠르게 업데이트 유지하는 방법이며, 해결책의 일부가 될 수 있습니다. 위의 작업을 수행 할 때마다 코드 행마다 스레드 호출을 잠 그거나 교차하는 것을 생각하지 않아도됩니다. –

+0

Backgroundworker를 보면 스레드를 생성하고 WindowsFormsSynchronizationContext로 마샬링하는 것과 큰 차이가 없으며 BW도 마찬가지입니다. –

+0

편집을 참조하십시오. – Dun3

0

이것은 내가 Update Controls에서 해결 한 문제입니다. 코드를 다시 작성하지 말고 아이디어를 볼 수있는 소스를 제공하기 위해이 코드를 작성합니다.

WPF에서 사용한 기술은 Dispatcher.BeginInvoke를 사용하여 포어 그라운드 스레드에 변경 사항을 알리는 것이 었습니다. Winforms에서 Control.BeginInvoke를 사용하여 동일한 작업을 수행 할 수 있습니다. 안타깝게도 Form 객체에 대한 참조를 데이터 객체에 전달해야합니다.

일단 작업을 수행하면 BeginInvoke에 PropertyChanged를 발생시키는 액션을 전달할 수 있습니다. 예 :

_form.BeginInvoke(new Action(() => NotifyPropertyChanged(propertyName)))); 

데이터 개체의 속성을 스레드로부터 안전하게 보호해야합니다.

2

예 모든 책이 스레드 보여 구조를 원용하는 등 등 완벽하게 정확하지만 코드 통증, 종종 하드 있도록 구성 할 당신이 그것을

UI를위한 괜찮은 테스트를 할 수 할 수 있습니다 만 초당 여러 번 새로 고쳐야하므로 성능은 결코 문제가되지 않으며 폴링은 정상적으로 작동합니다.

백그라운드 스레드 풀에 의해 지속적으로 업데이트되는 개체 그래프를 사용하고 싶습니다. 데이터 값의 실제 변경 사항을 확인하고 실제 변경 사항을 확인하면 개체 그래프의 루트 (또는 각 주 항목의 의미가 무엇이든간에)에서 버전 카운터를 업데이트하고 값을 업데이트합니다.

그런 다음 전경 프로세스 (기본값으로 UI 스레드와 동일)를 실행하여 버전 카운터를 확인하고 버전이 변경되면 잠금 (부분 업데이트 중지) 후 디스플레이 새로 고침

이 간단한 기술 백그라운드 스레드에서 UI 스레드를 완전히 격리합니다.

+0

나는 타이머가 과거에 잘 작동하는 것을 발견했다. –

1

나는 방금 BeginInvokes를 통해 UI를 업데이트 badkground 스레드 - 싸웠다. 배경은 모든 루프마다 10ms의 지연이 있지만 길 아래로 UI를 업데이트 할 때마다 루프에서 매번 실행되고 업데이트 빈도를 따라갈 수 없으며 앱이 효과적으로 작동을 멈 춥니 다 (무슨 일이 일어 났는지 확실하지 않아서 스택을봤을 까?).

나는 준비된 플래그였던 invoke를 통해 전달 된 객체에 플래그를 추가했습니다. 이 호출을 호출하기 전에 false로 설정하면 bg 스레드는이 플래그가 true로 다시 토글 될 때까지 ui 업데이트를 더 이상 수행하지 않습니다. UI 스레드는 화면 업데이트 등을 수행 한 다음이 var를 true로 설정합니다.

이렇게하면 bg 스레드가 계속 처리 할 수 ​​있지만 더 이상 준비가 끝날 때까지 UI가 흐름을 종료 할 수 있습니다.

+0

이 부분이 확실하지 않은 이유는 플래그가 두 스레드에서 액세스 할 수있는 이유입니다. 그러나 그것은 단지 작동하는 것처럼 보인다. –

0

새 UserControl을 만들고 컨트롤을 추가하고 서식을 지정할 수 있습니다 (dock = fill). 속성을 추가하십시오. 이제 원하는 모든 스레드의 속성 양식을 변경할 때마다 usercontrol을 호출하고 요소를 업데이트하는 속성을 구성하십시오! 난 내보기

viewTx.DataBindings.Add(new Binding("Value", ptx.CounterTX, "ReturnValue")); 
0

이 게시물 오래하지만 난 다른 사람에게 옵션을 제공 거라고 생각을 결합 내 양식에

private long value; 
    public long Value 
    { 
     get { return this.value; } 
     set 
     { 
      this.value = value; 

      UpdateTextBox(); 
     } 
    } 

    private delegate void Delegate(); 
    private void UpdateTextBox() 
    { 
     if (this.InvokeRequired) 
     { 
      this.Invoke(new Delegate(UpdateTextBox), new object[] {}); 
     } 
     else 
     { 
      textBox1.Text = this.value.ToString(); 
     } 
    } 

:

내 솔루션을 이잖아. 일단 비동기 프로그래밍 및 Windows Forms 데이터 바인딩을 시작하면 Bindingsource 데이터 소스를 업데이트하거나 Windows 양식 컨트롤에 바인딩 된 목록을 업데이트하는 데 문제가있는 것으로 보입니다. wintellect의 powerthreading 도구에서 Jeffrey Richters AsyncEnumerator 클래스를 사용해 보겠습니다.

이유 : 1.그의 AsyncEnumerator 클래스는 자동으로 백그라운드 스레드를 UI 스레드에 마샬링하므로 동기식 코드처럼 컨트롤을 업데이트 할 수 있습니다. 2. AsyncEnumerator는 비동기 프로그래밍을 단순화합니다. 이 작업은 자동으로 수행되므로 동기식으로 코드를 작성하지만 코드는 여전히 비동기식으로 실행됩니다.

Jeffrey Richter는 채널 9 MSDN에서 AsyncEnumerator를 설명하는 비디오를 가지고 있습니다.

행운을 빌어 요.

-R

관련 문제