2015-01-07 4 views
3

Я собирал небольшую небольшую демоверсию, чтобы использовать длительный метод, имитированный с помощью Thread.Sleep(), и хотел добавить асинхронный способ, чтобы легко перейти от синхронного процесса до асинхронного. Вот исходный код:Разница между Task.Delay() и новой задачей (() => Thread.Sleep())

private void button1_Click(object sender, EventArgs e) 
{ 
    LongProcess(); 
} 

private void LongProcess() 
{ 
    for (int i = 0; i < 33; i++) 
    { 
     progressBar1.Value += 3; 
     Thread.Sleep(1000); 
    } 
    progressBar1.Value += 1; 
} 

Я думал, что я мог бы просто изменить Thread.Sleep(1000) в new Task(()=>Thread.Sleep(1000)), например, так:

private void button1_Click(object sender, EventArgs e) 
{ 
    LongProcess(); 
} 

private async void LongProcess() 
{ 
    for (int i = 0; i < 33; i++) 
    { 
     progressBar1.Value += 3; 
     await new Task(()=>Thread.Sleep(1000)); 
    } 
    progressBar1.Value += 1; 
} 

Однако это никогда не возвращается в цикл после первого ОЖИДАНИЯ. Если я изменяю Thread.Sleep на Task.Delay, все работает, но я не понимаю, почему мой код не работает. Я предполагаю, что что-то блокируется навсегда, но это не совсем понятно. Может ли кто-нибудь объяснить, как работает мой код, и возможное решение, не изменяя Task.Delay (просто чтобы я мог получить другую перспективу, как это работает)?

+0

'ждут новую задачу (() => Thread.Sleep (1000))' это косвенный способ сделать то же самое, что и 'Thread.Sleep (1000)'. Кроме того, он несовместим с .Net 2.0/3.5 – i486

ответ

6

Task.Delay не то же самое, что смотреть на задачу с Thread.Sleep. Task.Delay использует Timer внутри и, следовательно, не блокирует ничью, однако запуск новой задачи с Thread.Sleep блокирует нить (обычно Threadpool thread).

В вашем примере вы никогда не запустили Task. При создании Task с конструктором возвращается задача Unstarted, которая должна быть запущена с вызовом метода Start. В противном случае он никогда не завершится (потому что вы его никогда не запускали).

Однако вызывая Task.Start обескураживают, Вы можете позвонить Task.Factory.StartNew(()=> Thread.Sleep(1000)) или Task.Run(()=> Thread.Sleep(1000)) если вы хотите тратить ресурс.

Кроме того, имейте в виду, что StartNew is dangerous, вы должны предпочесть Task.Run над StartNew, если нет веских оснований для этого.

+0

И это особенно проблематично, когда выполнение задачи передается в поток пользовательского интерфейса. – Euphoric

+0

@Euphoric Я думаю, теперь я обратился к вашему комментарию в своем ответе со ссылкой на блог Stephen. –

+0

Спасибо за информацию, я просто делаю это для академических целей, чтобы понять основные части работы, поэтому я ценю дополнительную информацию –

4

new Task(()=>Thread.Sleep(1000)) создает задачу, но не запускает ее.

Чтобы создать и запустить задачу, вы можете использовать Task.Run(() => Thread.Sleep(1000)) или Task.Factory.StartNew(() => Thread.Sleep(1000)).

+0

Да, это имеет смысл, я думал, что начать с подразумеваемого, если я его ждал. –

3

Чтобы ответить на вопрос название - Task.Delay можно отменить!

Рассмотрите популярную реализацию, используя TaskCompletionSource.

static Task Delay(int delayTime, System.Threading.CancellationToken token) 
    { 
     TaskCompletionSource<object> tcs = new TaskCompletionSource<object>(); 

     if (delayTime < 0) throw new ArgumentOutOfRangeException("Delay time cannot be under 0"); 

     System.Threading.Timer timer = null; 
     timer = new System.Threading.Timer(p => 
     { 
      timer.Dispose(); //stop the timer 
      tcs.TrySetResult(null); //timer expired, attempt to move task to the completed state. 
     }, null, delayTime, System.Threading.Timeout.Infinite); 

     token.Register(() => 
      { 
       timer.Dispose(); //stop the timer 
       tcs.TrySetCanceled(); //attempt to mode task to canceled state 
      }); 

     return tcs.Task; 
    } 

Вы не можете сделать это с помощью Thread.Sleep. Вы можете сделать это с большой старой петлей, но это просто эмулирует базовый Timer в коде выше.

Смежные вопросы