如何避免异步方法使用新线程?

时间:2018-05-10 09:45:20

标签: c# .net async-await

我已经读过await / async不应该创建/使用新线程,但是编译器会在引擎盖下创建一个精确的机制....所以我尝试了这个,每次调用F1都使用ThreadPool中的一个线程。

class Program
{
    static System.Threading.SemaphoreSlim sem =
        new System.Threading.SemaphoreSlim(20, 20);
    static async void F1()
    {
        await sem.WaitAsync();
        await sem.WaitAsync();
        await Task.Delay(6000);
        var threadId = GetCurrentThreadId();
        sem.Release();
        sem.Release();
    }

    static void Main(string[] args)
    {
        var threadId = GetCurrentThreadId();

        F1();
        F1();
        F1();
        F1();
        F1();
        F1();
        F1();
        F1();
        Task.Delay(30000).Wait();
    }
    [DllImport("kernel32.dll")]
    static extern uint GetCurrentThreadId();

}

2 个答案:

答案 0 :(得分:3)

你已经使用await Task.Delay(6000),保证不会同步完成,所以:完成将通过任何可能的方式返回 - 通常是线程池(因为这里没有同步上下文)。线程池线程很便宜,只要你不长时间绑定它们;如果它只是捕获当前的线程ID和释放,它们应该在它们发射后几乎立即完成。

代码需要在某处运行 ;你会期待运行什么线程?您的主要主题当前处于阻止Wait()调用中,因此它无法执行任何操作。注意:您几乎不应该在不完整的.Wait()上致电Task

答案 1 :(得分:1)

经过一番研究,我找到了我想要的答案。 https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/

事实上,Mrinal Kamboj在我的问题上添加了一条评论,指出了正确的方向。 我需要为继续运行创建同步上下文。这个片段还说明了为什么你不应该对不完整的任务进行Wait():如果延续在同一个线程上运行(对于GUI线程或ASP.NET来说),如果这个线程被锁定等待继续,那么我们就会死了 - 锁定情况。

class Program
{
    static System.Threading.SemaphoreSlim Semaphore = new System.Threading.SemaphoreSlim(3, 3);
    static async Task F(int i)
    {
        Console.WriteLine($"F{i} starts on " + GetCurrentThreadId());
        await Semaphore.WaitAsync();
        Console.WriteLine($"F{i} continues on " + GetCurrentThreadId());
        await Task.Delay(6000);     //.ConfigureAwait(false); //<- uncomment to solve deadlock
        Console.WriteLine($"F{i} ends on " + GetCurrentThreadId());
        Semaphore.Release(1);
        //DeadLock();
    }

    static void DeadLock()
    {
        F(70).Wait();
    }

    static void Main(string[] args)
    {
        var context = new SingleThreadSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(context); 
        for (int i = 0; i < 10; i++)
        {
            int ind = i;
            F(ind);
        }
        context.RunOnCurrentThread();
    }

    [DllImport("kernel32.dll")]
    static extern uint GetCurrentThreadId();

}


sealed class SingleThreadSynchronizationContext : SynchronizationContext
{

    private readonly Thread Thread = Thread.CurrentThread;


    public override void Post(SendOrPostCallback d, object state)
    {
        if (d == null)
            throw new ArgumentNullException("d");

        WorkItemsQueue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
    }


    public override void Send(SendOrPostCallback d, object state)
    {
        throw new NotSupportedException("Synchronously sending is not supported.");
    }


    public void RunOnCurrentThread()
    {
        foreach (var workItem in WorkItemsQueue.GetConsumingEnumerable())
            workItem.Key(workItem.Value);
    }


    public void Complete() { WorkItemsQueue.CompleteAdding(); }
}