2012-09-14 2 views
1

다음 코드는 SQL Server에서 가져온 고급 데이터 구조를 작성하는 데 필요한 데이터가 완료되면 UI를 업데이트합니다. 사용 된 코드는홀수 작업 병렬 라이브러리 InvalidOperationException

private void BuildSelectedTreeViewSectionAsync(TreeNode selectedNode) 
{ 
    // Initialise. 
    SqlServer instance = null; 
    SqlServer.Database database = null; 

    // Build and expand the TreeNode. 
    Task task = null; 
    task = Task.Factory.StartNew(() => { 
     string[] tmpStrArr = selectedNode.Text.Split(' '); 

     string strDatabaseName = tmpStrArr[0]; 

     instance = SqlServer.Instance(this.conn); 

     database = instance.GetDatabaseFromName(strDatabaseName); 
    }).ContinueWith(cont => { 
     instance.BuildTreeViewForSelectedDatabase(this.customTreeViewSql, 
      selectedNode, database); 

     selectedNode.Expand(); 

     task.Dispose(); 
    }, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, 
     this.MainUiScheduler); 
} 

입니다. 이것은 내 주요 개발 기계에서와 같이 작동합니다. 즉, database 개체의 빌드를 완료 한 다음 UI에서 UI를 업데이트하고 task (작업 개체)을 삭제합니다.

그러나, 나는 다른 컴퓨터에 대한 몇 가지 테스트를하고있다 그리고 내가 InvalidOperationException를 얻을,이 때문에 작업이 완료 될 때까지 실행하지 않는 한 계속 cont가 발생해서는 안 여전히 Running 상태에서, 그러나 tasktask.Dispose()이다 .

다음은 예외가 발생하면 코드가 디버거에서 어떻게 표시되는지를 보여줍니다 :

enter image description here

I am aware that it almost always unneccessary to call Dispose on tasks. ?이 질문은 당신이 위의 일을하려고하는 확신 **

답변

3

그 이유는, 당신이

귀하의 코드로 구성되어 첫 번째 작업에 계속 자체에 Dispose하지 호출 간단하다

Task task = null; 
var task = <task 1>.ContinueWith(t => { 
    /* task 2 */ 

    task.Dispose(); 
}); 

위의 코드에서 task은 연속 (ContinueWith은 원래의을 되돌려 보내지 않습니다., 그것은 계속을 통과합니다) 그리고 그것은 여러분이 닫는 부분에서 캡처 한 것이고 ContinueWith입니다.

당신은 task으로 ContinueWith 방법에 전달 된 Task 매개 변수의 참조를 비교하여이를 테스트 할 수 있습니다

Task task = null; 
var task = <task 1>.ContinueWith(t => { 
    /* task 2 */ 
    if (object.ReferenceEquals(t, task)) 
     throw new InvalidOperationException("Trying to dispose of myself!"); 

    task.Dispose(); 
}); 

을 처음 처분하기 위해, 두 Task 변수로 그것을 깰 필요 과 같이, 최초의 Task을 캡처 :

var task1 = <task 1>; 
var task2 = task1.ContinueWith(t => { 
    // Dispose of task1 when done. 
    using (task1) 
    { 
     // Do task 2. 
    } 
}); 

을하지만, previous Task는 01,230에서 매개 변수로 사용자에게 전달되기 때문에방법, 당신은 단순히 당신에게 매개 변수로 전달 된 TaskDispose를 호출 할 수 있습니다, 모든 폐쇄에 task을 캡처 할 필요가 없습니다 : 사실에 대한 참조를 위해

var task = <task 1>.ContinueWith(t => { 
    // t = task 1 
    // task = task 2 
    // Dispose of task 1 when done. 
    using (t) 
    { 
     // Do task 2. 
    } 
}); 
+0

나는이 사실을 믿지 않는다. 첫 번째 스 니펫에서 수행하는 방식으로 작업 및 연속을 정의 할 수 없습니다. 연속에서'task'를 참조하기 위해서는'Task'의 인스턴스를 생성 한 다음 그것에 할당해야합니다. ContinueWith (cont => {task.Dispose();});'task'가 범위 내에 있지 않으므로 _never_ 작업 할 것입니다 (var task = TaskFactory.StartNew ... (() => {...}). '. 'var task = null;''task = TaskFactory.StartNew ... (() => {...}) ContinueWith (cont => {task.Dispose();}); 'Task' 객체'task'에 행위를 버리십시오, 내가 여기에 정신을 보지 않으면 ... -] – MoonKnight

+0

Ps. 시간 내 주셔서 감사합니다 ... – MoonKnight

+0

@Killercam 코드를 업데이트했습니다. 첫 번째 코드 단편은 코드의 복제였으며 맞습니다. 변수가 먼저 선언 되어야만 클로저에서 액세스 할 수있었습니다. * 중요한 부분은 아니지만 *. – casperOne

1

계속 전혀 여기에 해고 이유에 대한 더에 equivelent입니다 :

task = Task.Factory.StartNew(() => ...); 
task.ContinueWith(cont => { ... task.Dispose(); }); 

그러나 할당됩니다 어떤 변수를 작업에 코드는 Origninal StartNew 작업 항목이 아닌 ContinueWith 작업 항목이됩니다.

더 중요한 것은이 시나리오에서 task.Dispose()에 대해 걱정할 필요가 없을 것입니다.

task.Dispose()를 수행하는 데 실제 값이있는 유일한 시간은 커버 아래에 OS 대기 핸들 리소스를 할당하는 task.Wait()이 어딘가에있을 때입니다.

상세 정보 : http://social.msdn.microsoft.com/Forums/en/parallelextensions/thread/7b3a42e5-4ebf-405a-8ee6-bcd2f0214f85

+0

한 그 폐기() 여기에 중복됩니다. 내 질문에 좋은 기사를 게시했습니다. 그러나, 나는 당신이 작업 참조에 대해 쓴 것이 옳다고 믿지 않습니다. 'Task t = TaskFactory.StartNew (() => {...}). ContinueWith (c => t.Dispose());를 실행하면 위의 코드를 사용하십시오. 그러나, 'Task t = null', 't = TaskFactory.StartNew (() => {...}) ContinueWith (c => t.Dispose());'가되고 원래의 't', _not_ 계속 ... – MoonKnight

관련 문제