2012-04-10 2 views
7

~ 1 초 기다리고 이전 사진을 복원하는 것보다 Windows Forms 창에서 사진을 대체 할 스레드를 만들어야합니다.잠자기 작업 (System.Threading.Tasks)

TaskScheduler ui = TaskScheduler.FromCurrentSynchronizationContext(); 
var task = Task.Factory.StartNew(() => 
{ 
    pic.Image = Properties.Resources.NEXT; 
    Thread.Sleep(1000); 
    pic.Image = Properties.Resources.PREV; 
}, CancellationToken.None, TaskCreationOptions.LongRunning, ui) 

이 일을하지만, 불행히도하지 않습니다

나는 다음과 같은 코드를 생각했다. 그것은 메인 UI 스레드를 고정시킵니다.

하나의 작업 당 하나의 스레드가 있다는 보장이 없기 때문입니다. 하나의 스레드는 여러 작업을 처리하는 데 사용할 수 있습니다. TaskCreationOptions.LongRunning 옵션도 도움이되지 않습니다.

어떻게 해결할 수 있습니까?

+8

WinForms는 UI 스레드가 될 'FromCurrentSynchronizationContext()'를 사용하여 작업 스케줄러를 생성합니다. 따라서 UI 스레드에서 작업이 실행되고 잠자기 모드로 전환됩니다. * UI 스레드를 잠자기 상태로 두지 마십시오. 적 * – dlev

+0

코드가 UI를 직접 업데이트하므로 UI ​​스레드에서 실행해야합니다. 코드가 잠자기되어 UI 스레드에서 실행할 수 없습니다. 결론 : 코드가 손상되었습니다. 충돌하는 두 가지 요구 사항 중 하나를 제거하여 문제를 해결하십시오. –

답변

25

Thread.Sleep은 동기 지연입니다. 비동기 지연을 사용하려면 Task.Delay을 사용하십시오.

는 베타 버전에서 현재는 C# 5에서, 당신은 단순히 비동기 방식으로

await Task.Delay(whatever); 

을 말할 수 있고,이 중단 된 방법이 자동으로 선택됩니다.

C# 5를 사용하지 않는 경우 지연 지속을 원하는 코드를 "수동으로"설정할 수 있습니다.

+0

나는이게 잘 작동합니다 .net 4.5 : –

+0

@JosephLennox C# 5 ==. NET 4.5 –

5

향후 어느 시점에서 UI 작업을 수행하려면 Timer을 사용해야합니다. 한 번 실행하고 1 초 간격으로 실행하도록 설정하십시오. 틱 이벤트에 UI 코드를 넣은 다음이를 설정하십시오.

당신이 정말로이 작업을 사용하고 싶었 경우, Control.Invoke를 사용 후 (즉, 단지 일반 StartNew 작업) UI 스레드에서가 아니라 배경의 위협에서 실행이 아닌 다른 작업을해야 할 것 작업 내에서 UI 스레드에서 명령을 실행합니다. 여기서 문제는 바로 '잠을 자도록하기 위해 업무 시작의 근원적 인 문제를 반창고하는 것'입니다. 코드가 전체 초 동안 처음부터 실행되지 않도록하는 것이 좋습니다.

7

현재 동기화 컨텍스트에서 새 TaskScheduler를 전달하면 실제로 UI 스레드에서 실행되도록 작업을 알립니다. 실제로 그렇게하기를 원하기 때문에 UI 구성 요소를 업데이트 할 수 있습니다. 그러나 차단할 것이므로 해당 스레드에서 잠자고 싶지는 않습니다.

이 좋은 .ContinueWith 이상적인 경우의 예입니다

TaskScheduler ui = TaskScheduler.FromCurrentSynchronizationContext(); 
var task = Task.Factory.StartNew(() => 
            { 
             pic.Image = Properties.Resources.NEXT; 
            }, 
           CancellationToken.None, 
           TaskCreationOptions.None, 
           ui); 

task.ContinueWith(t => Thread.Sleep(1000), TaskScheduler.Default) 
    .ContinueWith(t => 
         { 
          pic.Image = Properties.Resources.Prev; 
         }, ui); 

EDIT (일부 재료를 제거하고이를 추가) :

어떤 일이 것은 우리가 UI 스레드를 차단하고 있다는 것입니다 pic.Image을 업데이트하기위한 충분한 시간 동안. TaskScheduler을 지정하면 작업을 실행할 스레드를 알 수 있습니다. 작업과 스레드 간의 관계가 1 : 1이 아니라는 것을 아는 것이 중요합니다. 사실, 1000 개의 작업을 상대적으로 적은 스레드 (10 개 이하)에서 실행하도록 할 수 있습니다. 모든 작업은 각 작업의 작업량에 따라 다릅니다. 작성한 각 태스크가 별도의 스레드에서 실행된다고 가정하지 마십시오. CLR은 자동으로 성능을 균형있게 조정합니다.

이제 보았 듯이 기본값 TaskScheduler을 사용할 필요가 없습니다. UI를 으로 전달하면 TaskScheduler.FromCurrentSynchronizationContext()이되고 TaskScheduler.Default처럼 스레드 풀 대신 UI 스레드가 사용됩니다. 이 점을 염두에 유지

,의 코드를 다시 살펴 보자 : 여기

var task = Task.Factory.StartNew(() => 
            { 
             pic.Image = Properties.Resources.NEXT; 
            }, 
           CancellationToken.None, 
           TaskCreationOptions.None, 
           ui); 

을, 우리가 만들고 UI 스레드에서 실행하는 작업을 시작하고, 그 picImage 속성을 업데이트합니다 당신의 자원으로 이 작업을 수행하는 동안 UI이 응답하지 않습니다.다행히도이 경우 이 매우 빠르며 빠른 작업이므로 사용자는 알지도 못합니다.

task.ContinueWith(t => Thread.Sleep(1000), TaskScheduler.Default) 
    .ContinueWith(t => 
         { 
          pic.Image = Properties.Resources.Prev; 
         }, ui); 

이 코드에서는 ContinueWith 메서드를 호출합니다. 그것은 정확하게 그것이 들리는 것을한다. 실행될 때 람다 매개 변수를 실행할 새로운 Task 개체를 반환합니다. 작업이 완료되거나 오류가 발생하거나 취소되면 작업이 시작됩니다. TaskContinuationOptions을 전달하면 실행시기를 제어 할 수 있습니다. 그러나 이전과 다른 작업 스케줄러를 전달합니다. 이것은 스레드 풀 스레드에서 작업을 실행하는 기본 작업 스케줄러이므로 UI를 차단하지 않습니다. 이 작업은 몇 시간 동안 실행될 수 있으며 상호 작용하는 UI 스레드와는 별도의 스레드이기 때문에 UI가 응답 성을 유지합니다 (허용하지 마십시오).

또한 우리는 기본 작업 스케줄러에서 실행하도록 설정 한 작업에 ContinueWith을 호출했습니다. 이것은 동일한 UI 작업 스케줄러를 실행중인 작업에 전달했기 때문에 UI 스레드에서 이미지를 다시 업데이트하는 작업입니다. 스레드 풀 작업이 끝나면 UI 스레드에서이 작업을 호출하여 이미지가 업데이트되는 동안 매우 짧은 시간 동안 스레드 스레드를 차단합니다.

+0

사실 저는'PictureBox'es와 함께 큰'TableLayoutPanel'을 가지고 있기 때문에 타이머를 사용하지 않습니다. ('PictureBox's만큼 많은 타이머를 갖고 싶지 않습니다.) 1 초 동안 그림을 바꾸고 싶기 때문에이 작업 코드를 OnClick 이벤트 처리기에 배치했습니다. 클릭 한 그림을 클릭 한 후 –

+0

코드가 잘 작동하지만 이유를 이해할 수 없습니다. 해결책을 얻는 열쇠는 다른 TaskScheduler ('.Default')를 사용하여 계속 진행하는 작업입니다. 무슨 뜻이에요? 컨텍스트를 스레드 풀 스레드 컨텍스트로 변경 하시겠습니까? 그래서 우리는 나머지 스레드를 멈 춥니 다? 그건 분명하지 않아. –

+0

@ patryk.beza - 내 편집으로 인해 더 명확 해집니다. –

관련 문제