2016-11-14 2 views
1

Я думал, что вызов async-метода с использованием Task.Run не вызывает никаких взаимоблокировок. Но это так:Вызов метода асинхронного синхронного использования Task.Run завершается в тупике

async Task<string> GetIdAsync() 
{ 
    return Task.Run(() => IncInt64Async("id", 1L)).Result.ToString(); // Deadlock 
} 

async Task<Int64> IncInt64Async(string key, Int64 inc); 

Я знаю, что должен использовать ожидание. Так это работает:

async Task<string> GetIdAsync() 
{ 
    return await IncInt64Async("id", 1L); // of course, no deadlock. 
} 

Но я просто пытаюсь понять, почему первая попытка вызывает затор. В моем понимании async, тупик происходит только тогда, когда метод async захватывает контекст синхронизации и пытается вернуться к контексту, пока контекст не может его разрешить.

Кроме того, GetIdAsync() вызывается внутри Task.Run(), так что это все равно работает без синхронизации контекста:

Task.Run(() => { 
    some code... 
    id = getIdAsync(); 
    some code... 
} 
); 

Так что я сошел с ума, и перепробовали все варианты, я мог думать о, но до сих пор все попытки вызывает тупики:

Task.Run(() => IncInt64Async("id", 1L).ConfigureAwait(false).GetAwaiter().GetResult()).ConfigureAwait(false).GetAwaiter().GetResult(); 
IncInt64Async("id", 1L).ConfigureAwait(false).GetAwaiter().GetResult(); 
Task.Run(() => IncInt64Async("id", 1L)).Result; 
Task.Run(() => IncInt64Async("id", 1L)).ConfigureAwait(false).GetAwaiter().GetResult(); 
Task.Run(async() => await IncInt64Async("id", inc)).ConfigureAwait(false).GetAwaiter().GetResult(); 
Task.Run(() => { var task = IncInt64Async("id", inc); task.Wait(); return task.Result; }).Result; 

КСТАТИ Внутри реализации IncInt64Async, он использует несколько ждать, не ConfigureAwait (ложь).

Что может быть причиной?


Реализация IncInt64Async():

async Task<Int64> IncInt64Async(string key, Int64 value) 
{ 
    Task task = null; 
    byte error = 1; 
    byte[] bytes = null; 
    lock (c.WriteLock) 
    { 
     c.Write(key, value); 

     _readTask = task = _readTask.ContinueWith(async o => 
     { 
      if ((error = await c.ReadByteAsync()) == 0) // **BLOCKS HERE** 
      { 
       bytes = new byte[ 8 ]; 
       await c.ReadAsync(bytes, 0, 8); 
      } 
      NumWaits--; 
     }).Unwrap(); 
    } 

    if (task != null) 
     await task.AnyContext(); 

    if (error != 0) 
     throw new InvalidOperationException(); 

    return BitConverter.ToInt64(bytes, 0); 
} 

'с' является экземпляром TcpHandler.

Он в основном пишет над tcp и читает от tcp.

Я использую ContinueWith() для выравнивания показаний. Я пробовал пропустить TaskScheduler.Default для ContinueWith().

+0

Откуда вы это называете? Приложение UI, приложение asp.net, консоль? – Evk

+0

@Evk это из консольного приложения. Даже GetIdAsync() вызывается внутри Task.Run следующим образом: Task.Run (() => id = GetIdAsync(); ...) – wooohoh

+0

Если это консольное приложение, то как оно фактически блокируется? Тупик с асинксом обычно происходит в приложениях с некоторым понятием потока «пользовательского интерфейса» или «запроса», а не в консольных приложениях. – Evk

ответ

0

Я пробовал все варианты, которые вы предложили в консольном приложении на LinqPad, и ничего не блокируется, поэтому я сделал предположение относительно IncInt64Async, но нет тупика, измените это, чтобы показать, что блокируется. Это исключительно для обучения, это, безусловно, не очень хорошие реализации для async-await

void Main() 
{ 
    Task.Run(() => GetIdAsync()).Result.Dump(); 
} 

async Task<string> GetIdAsync() 
{ 
    // return Task.Run(() => IncInt64Async("id",1L)).Result; // 1. 
    // return IncInt64Async("id", 1L).ConfigureAwait(false).GetAwaiter().GetResult(); // 2. 
    // return Task.Run(() => IncInt64Async("id",1L).ConfigureAwait(false).GetAwaiter().GetResult()).ConfigureAwait(false).GetAwaiter().GetResult(); // 3. 
    // return Task.Run(() => IncInt64Async("id",1L)).ConfigureAwait(false).GetAwaiter().GetResult(); // 4. 
    // return Task.Run(async() => await IncInt64Async("id", 1L)).ConfigureAwait(false).GetAwaiter().GetResult(); // 5. 
    return Task.Run(() => { var task = IncInt64Async("id", 1L); task.Wait(); return task; }).Result; // 6. 
} 

public async Task<string> IncInt64Async(string id, long l) 
{ 
    await Task.Delay(1000); 

    return "Test"; 
} 
+0

Спасибо за ваши усилия. Я также попытался сделать минимальный пример, но он просто работает нормально, как вы заявили. Похоже, что детали реализации IncInt64Async() вызывают тупик. – wooohoh

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