2010-02-16 3 views
1

는 최근 개발자가 내 라이브러리를 소비 할 때, 그들은 다음에 대해 걱정할 필요가 없습니다 있도록 비동기 콜백 모델을 구현하기 위해 시도했다 : 코드의 그 부분은 확실히 작동하는 것 같군 비동기 Post + 콜백은 실제로 대기열을 방해합니까?

if(control.InvokeRequried) { // invoke } 

좋았지 만 오늘은 좀 이상한 것을 발견했습니다. 나는 진단을 받았지만 더 많은 경험을 가진 다른 사람들이 생각하는 것을보고 싶습니다. 그리고 그것을 해결할 수있는 방법도 있습니다.

내가 겪고있는 문제는 내 게시물이 어떤 이유로 인해 매우 느린 것 같아 결과적으로 내 작업을 완료 한 후에 약간 막히고 계속 작동하는 것으로 보입니다. 간단한 코드 예제 :

// called by user 
WorkerEventHandler workerDelegate = new WorkerEventHandler(CalcAsync); 
workerDelegate.BeginInvoke(p, asyncOp, null, null); 

CalcAsync(P p, AsyncOperation asyncOp) 
{ 
    Calculate(); 
    asyncOp.PostOperationCompleted(new AsyncCompletedEventArgs(null, false, 
     asyncOp.UserSuppliedState); 
} 

Calculate() 
{ 
    DoStuff()   // updates the progress as it goes 
    this.UpdateStatus("Done"); // marks us as done 
    this.UpdateProgress(pId, 100); // increase progress to max 
} 

내가 그러나 게시 된 이벤트에서 호출되는 의 UpdateProgress에 호출을 반복 얻을 단계별, 그래서 그들은 유지할 수 없었습니다 것 같습니다, 여전히 게재되고있다 . 이들은 다음과 같이 처리됩니다 :

// initalized with 
// onProgressUpdatedDelegate = new SendOrPostCallback(UpdateProgress);  
private SendOrPostCallback onProgressUpdatedDelegate; 
public void UpdateProgress(Guid pId, ProgressEventArgs e) 
{ 
    AsyncOperation asyncOp = GetAsyncOperation(pId); 

    if (asyncOp != null) 
    { 
     asyncOp.Post(this.onProgressUpdatedDelegate, e); 
    } 
    else 
    { 
     UpdateProgress(this, e); 
    } 
} 

private void UpdateProgress(object state) 
{ 
    ProgressEventArgs e = state as ProgressEventArgs; 
    UpdateProgress(this, e); // does the actual triggering of the EventHandler 
} 

이 이벤트는 모두 콘솔에 기록됩니다. 하지만 내 진행 상황이 내 작업이 WPF 테스트 응용 프로그램을 통해 볼 수있는 것과 동일한 결과 인 것으로 판단되는 더 긴 작업에만 적용되는 것으로 보입니다.

게시물의 속도가 느린 것을 제안합니까? 내가 전화를 할 때마다 내가 전화하고 싶을 때마다 UpdateProgress 게시판을 통해 대기중인 게시물을 삭제하고 싶습니다. 그러나 이것이 가능한지 확신 할 수 없습니까? 이 경우, 내가 다음 이벤트 실수로 UpdateStatus을 제거하지 않는 한 ..., 이러한 이벤트를 단지

편집

그 경우 누락 된 방법의 일부를 제거 할 수 있습니다 쉽게 해줍니다.

public void UpdateProgress(Guid pId, ProgressEventArgs e) 
{ 
    AsyncOperation asyncOp = GetAsyncOperation(pId); 
    if (asyncOp != null) 
    { 
     asyncOp.Post(this.onProgressUpdatedDelegate, e); 
    } 
    else 
    { 
     UpdateProgress(this, e); 
    } 
} 

private void UpdateProgress(object sender, ProgressEventArgs e) 
{ 
    ProgressChangedEventHandler handler; 
    lock (progressUpdateEventLock) 
    { 
     handler = progressUpdateEvent; 
    } 

    if (handler != null) 
     handler(sender, e); 
} 

답변

1

여기에 무슨 일이 일어나는지 명확하지 않습니다. 작업자 스레드에서 하나의 UpdateProgress 호출에 대한 UI 스레드에서 여러 UpdateProgress 호출을 얻는 것은 확실히 잘못되었습니다. 게시 된 코드가이 작업을 수행하면 안됩니다.

하지만 그렇습니다. Post() 호출은 특별히 빠르지 않습니다.UI 스레드의 메시지 루프가 알림을 선택하는 데 약 1 밀리 초가 걸리고 다른 작업을 수행하는 것이 바쁘지 않다고 가정합니다. 이것은 하드 CPU 작업의 1 밀리 초가 아니라 GetMessage()를 처리하는 Windows 배관 작업에 의해 유발되는 지연입니다. 쓰레드 컨텍스트 스위치는 그것의 일부이지만, 단지 작은 부분이다.

이것은 부작용이 현저 할 수 있습니다. 너무 자주 Post()를 호출하거나 게시 된 델리게이트가 너무 많은 노력을 기울이는 것은 UI 스레드에 심각한 영향을 미칠 수 있습니다. 요즘은 더 이상 일상적인 의무를 다하지 못한다. 지체로 인해 빠르게 도착할 수 있습니다. 초당 1000 개의 게시물 만 필요합니다.

물론, 사람의 눈은 초당 약 25보다 빠른 속도로 업데이트를 인식 할 수 없다는 것을 자주 게시 할 필요는 없습니다. 더 자주하는 것은 낭비입니다. 그러나 작업자 스레드가 경과 시간에 특별한주의를 기울이지 않으면이 이상적인 업데이트 속도를 얻는 것이 특히 쉽지는 않습니다.

Anyhoo, 마샬링 된 알림 방법을 알려주는 것이 클라이언트 코드에 맡기는 것이 좋습니다. FileSystemWatcher.SynchronizingObject는 그 좋은 예입니다. Post()가 분리되면 클라이언트 코드가 문제를 해결할 수있는 방법 중 하나입니다. 또한 BackgroundWorker 클래스를 살펴보십시오.

+0

nobugz, 귀하의 의견에 감사드립니다. 그것은 속도가 느린 포스트 속도 일 수 있습니다. 현재 그것이 그 원인인지 확실하게 판단 할 수 있는지 알아 내려고합니다. 꼭 필요한 경우 클래스 소비자가 Invokes를 관리하도록 제안 하시겠습니까? 내 목표는 트리거 된 이벤트에서 UI 업데이트를 호출 할 필요가없는 WebClient.DownloadAsync() 메서드와 같은 것입니다. – Ian

+0

나는 당신에게 클라이언트에게 선택권을 주기만을 제안하고있다. DownloadAsync()는 관리하기 쉽고 완료 이벤트는 하나뿐입니다. –

+0

nobugz, 단일 완료 이벤트 만 있어야합니다. 그것은 단지 WebClient.OnProgressChanged 이벤트처럼 약간의 발생이 예상되는 진행/상태/솔루션을 알리는 3 가지 다른 이벤트가 있습니다. 또한 분명히하기 위해 단일 작업자 스레드 호출로 여러 UI 호출이 있다고 생각하지 않지만 너무 빨라야하는 workerthread 호출이 많습니다. 되돌리기를 고려하고 클라이언트가 이상한 진행 표시 줄보다 더 나은 것을 호출하도록 할 것입니다! – Ian

0

예제에 몇 가지 코드가 누락 되었습니까? UpdateProgress(this, e)에 대한 전화가 있지만 해당 서명이있는 해당하는 방법이 없습니다. thisGuid이 아니라면, 그때 나는 무한 재귀 적 방법으로 끝날 것입니다. UpdateProgress(100)에 대한 전화가 있으며 해당하는 방법이 없습니다.

어쨌든 프로파일 러를 사용하거나 Stopwatch이 없으면 메소드에서 시간을 보냈다면 유용 할 것입니다.

InvokeRequired을 수행하고 모든 업데이트를 수행 할 때마다 Invoke을 수행해야하기 때문에 스레드 컨텍스트 스위치로 인해 사용자를 죽일 수 있습니다. 그렇다면 Post을 대기열에 넣고 주기적으로 대기열을 비우는 타이머를 사용하는 것이 도움이 될 것입니다. 예 :

Timer queueTimer = new Timer(QueueTimerProc, null, 20, 20); 
queueTimer.Start(); 

void QueueTimerProc(object state) 
{ 
    if (queue.Count > 0) 
    { 
     if (this.InvokeRequired) 
      // Invoke the EmptyQueue method 
     else 
      // Call the EmptyQueue method 
    } 
} 

void EmptyQueue() 
{ 
    lock (queue) 
    { 
     while (queue.Count > 0) 
     { 
      // dequeue an item and post the update 
     } 
    } 
} 
+0

누락 된 방법을 포함하도록 게시물을 업데이트했습니다. UpdateProgress (100)도 업데이트되어 실제로 pId 값을 전달합니다. 현재 'InvokeRequired'체크를하지 않았으므로, WinForm 컨트롤에서만 사용할 수 있다고 믿습니다. 이것은 클래스 라이브러리 내에 있습니다. 내가 목표로 삼은 것은 최종 사용자가 호출을 사용하여 클래스 라이브러리에서이 모든 것을 처리 할 필요가 없다는 것입니다. WebClient를 사용하여 파일을 비동기 적으로 다운로드하는 것처럼, UI에 대한 업데이트 호출을 호출 할 필요가 없습니다. 즉, 내가 목표로했던 것입니다. – Ian

관련 문제