2016-08-17 7 views
0

나는 장기 실행, 블로킹, 불안정 및 \ 또는 영원히 걸릴 가능성이있는 별도의 스레드에서 코드를 실행해야하는 경우가 많습니다. TPL의 존재 이후 인터넷은 취소 토큰을 사용하여 작업을 멋지게 취소하는 예제로 가득차 있지만 중단 된 작업을 죽이는 예제는 발견하지 못했습니다. 영원히 중단되는 코드는 하드웨어와 통신하거나 제 3 자 코드를 호출하자 마자 예상됩니다. 중단 된 작업은 취소 토큰을 확인할 수 없으며 영원히 살아 남을 운명입니다. 중요한 어플리케이션에서 나는 정기적 인 시간 간격으로 보내지는 살아있는 신호로 작업을 준비한다. 걸려있는 작업이 감지 되 자마자 작업이 중단되고 새 인스턴스가 시작됩니다.매달린 작업을 보장하는 방법이 보장 되었습니까?

아래 코드는 영원히 멈출 가능성이있는 장기 실행 자리 표시 자 메서드 SomeThirdPartyLongOperation()을 호출하는 예제 작업을 보여줍니다. StopTask()는 작업이 아직 실행 중인지 여부를 먼저 확인하여 해지 토큰을 사용하여 작업을 취소하려고 시도합니다. 그래도 작동하지 않으면 작업이 중단되고 기본 스레드가 중단되어 \ 중단 된 이전 학교 스타일이됩니다.

private Task _task; 
    private Thread _thread; 
    private CancellationTokenSource _cancellationTokenSource; 

    public void StartTask() 
    { 
     _cancellationTokenSource = new CancellationTokenSource(); 
     _task = Task.Factory.StartNew(() => DoWork(_cancellationTokenSource.Token), _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); 
    } 

    public void StopTask() 
    { 
     if (_task.Status == TaskStatus.RanToCompletion) 
      return; 
     _cancellationTokenSource.Cancel(); 
     try 
     { 
      _task.Wait(2000); // Wait for task to end and prevent hanging by timeout. 
     } 
     catch (AggregateException aggEx) 
     { 
      List<Exception> exceptions = aggEx.InnerExceptions.Where(e => !(e is TaskCanceledException)).ToList(); // Ignore TaskCanceledException 
      foreach (Exception ex in exceptions) 
      { 
       // Process exception thrown by task 
      } 
     } 
     if (!_task.IsCompleted) // Task hangs and didn't respond to cancellation token => old school thread abort 
     { 
      _thread.Interrupt(); 
      if (!_thread.Join(2000)) 
      { 
       _thread.Abort(); 
      } 
     } 
     _cancellationTokenSource.Dispose(); 
     if (_task.IsCompleted) 
     { 
      _task.Dispose(); 
     } 
    } 

    private void DoWork(CancellationToken cancellationToken) 
    { 
     if (string.IsNullOrEmpty(Thread.CurrentThread.Name)) // Set thread name for debugging 
      Thread.CurrentThread.Name = "DemoThread"; 
     _thread = Thread.CurrentThread; // Save for interrupting/aborting if thread hangs 
     for (int i = 0; i < 10; i++) 
     { 
      cancellationToken.ThrowIfCancellationRequested(); 
      SomeThirdPartyLongOperation(i); 
     } 
    } 

몇 년 동안이 구조를 사용해 왔지만 몇 가지 잠재적 인 실수가 있는지 알고 싶습니다. 기본 스레드를 저장하거나 디버깅을 단순화하는 이름을 제공하는 태스크의 예는 본 적이 없으므로 이것이 올바른 방향인지 확실하지 않습니다. 어떤 세부 사항든지에 코멘트는 환영 받다!

답변

0

질문은 왜이 작업도 전혀 걸려 있지 않습니까? 나는이 문제에 대한 보편적 인 해결책이 없다고 생각하지만, 당신은 항상 책임감을 지키고 그것을 방해하지 않는 임무에 집중해야한다.

이 코드에서는 작업이 아니라 단순한 스레드를 찾고있는 것처럼 보입니다. 스레드를 작업에 연결하면 안됩니다. 일부 비동기 작업 후에 다른 스레드로 전환 될 가능성이 매우 높습니다. 더 이상 당신의 업무에 연결되지 않은 무고한 실을 죽이는 결과를 낳을 것입니다. 전체 스레드를 정말로 죽일 필요가 있다면이 작업을 위해 전용 스레드를 만드십시오.

작업의 기본 풀에 사용되는 스레드의 이름을 지정하거나 작업을 수행하면 안됩니다. 이 코드를 고려하십시오

static void Main(string[] args) 
{ 
    Task.Run(sth); 
    Console.Read(); 
} 

static async Task sth() 
{ 
    Thread.CurrentThread.Name = "My name"; 
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId); 
    await Task.Delay(1); 
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId); 
    Console.WriteLine(Thread.CurrentThread.Name ?? "No name"); 
} 

출력은 다음과 같습니다 영원히 중단

3 
4 
No name 
+0

의견을 보내 주셔서 감사합니다. 자신의 신뢰할 수있는 영역을 떠나서 일부 타사 소프트웨어를 통합하자 마자 가끔이 소프트웨어가 멈추는 현실 세계가 _ 있습니다. 별도의 스레드에서이 소프트웨어를 실행하면 주 소프트웨어에 타사 소프트웨어에 대한 추가 제어 권한을 부여 할 수 있습니다. 스레드 풀에서 다른 스레드가 할당 될 수 있음을 알고 있지만 코드에 대기 중이므로이 작업이 LongRunning이며 따라서 전용 스레드 [link] (https://coderkarl.wordpress.com)에 할당되었음을 알고 있습니다./2012/12/13/long-running-tasks-and-threads /) –

1

코드는 바로 하드웨어와 통신하거나 일부 타사 코드를 호출로 예상 될 가능성이 높습니다.

의사 소통 : 절대적으로. 통신 API를 사용하여 시간 초과하는 방법은 항상 있으므로 하드웨어를 오작동하는 경우에도 I/O 작업을 강제 종료 할 필요가 없습니다.

타사 코드 : 편집증 환자이거나 24x7 자동화와 같은 높은 요구 사항이있는 경우에만. 작업을 강제로 죽일 수있는 방법이

  • 없습니다 :

    여기에 결론입니다.

  • 스레드를 강제 종료 할 수는 있지만 응용 프로그램 상태에 심각한 문제가 발생할 수 있으며 코드의 다른 부분에 교착 상태가 발생할 가능성이 있으며 리소스가 누출 될 수 있습니다.
  • appdomain을 강제 종료 할 수 있습니다. 이로 인해 스레드를 죽이는 데있어 앱 상태/교착 상태 문제가 상당 부분 해결됩니다.그러나 모든 문제를 해결하지 못하고 리소스 누수 문제가 여전히 있습니다.
  • 프로세스를 강제 종료 할 수 있습니다. 이것은 유일하고 진정으로 신뢰할 수있는 솔루션입니다.

타사 코드를 신뢰하도록 선택한 경우 다른 API와 마찬가지로 호출하는 것이 좋습니다. 타사 라이브러리와 상관없이 100 %의 안정성이 필요한 경우 타사 DLL을 별도의 프로세스로 래핑하고 프로세스 간 통신을 사용하여 호출해야합니다.

현재 코드가 강제로 스레드 풀 스레드를 강제 종료합니다. 이러한 스레드는 사용자가 아닌 스레드 풀에 속하므로 LongRunning을 지정해도 여전히 해당됩니다. kill-thread 라우트 (완전히 신뢰할 수는 없음)로 간다면 명시 적 스레드를 사용하는 것이 좋습니다.

+0

[Glad Lucia]의 기사 (https://coderkarl.wordpress.com/2012/12/13/long-running-tasks-and) -threads /) 및 [Bar Arnon] (http://blog.i3arnon.com/2015/07/02/task-run-long-running/)에서는 LongRunning 작업이 해당 스레드에 속하지 않는 전용 스레드에서 실행된다는 것을 명확하게 설명합니다. 스레드 풀 귀하의 블로그 [StartNew is dangerous] (http://blog.stephencleary.com/2013/08/startnew-is-dangerous.html)에서 당신은 그것을 '_ 현재의 구현에서 그런 식으로 일어난다. 이제는 무엇이 사실입니까? 구식 스레딩은 TPL처럼 취소를 지원하지 않으며 그 기능을 좋아합니다! –

+0

@ErikStroeken : 블로그는 공식 MSDN 설명서가 아닙니다. 새 스레드를 시작하는'LongRunning'은 구현 세부 사항입니다. –