简体   繁体   English

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

[英]How avoid async method to use new thread?

I have read that await/async should not create/use new threads but the compiler creates a astate machien under the hood....so i tried this, and each call to F1 uses a thread from the ThreadPool. 我读过await / async不应创建/使用新线程,但编译器会在后台创建astate machien。...所以我尝试了此操作,每次对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();

}

You've used await Task.Delay(6000) , which is guaranteed to not complete synchronously, so: the completion will come back via whatever it can - typically the thread-pool (since there's no sync-context in play here). 您已经使用了await Task.Delay(6000) ,它保证不会同步完成,因此:完成将通过任何可能的方式(通常是线程池)返回(因为此处没有同步上下文)。 Thread pool threads are cheap as long as you don't tie them up for extended periods; 只要您不长时间捆绑线程池线程,它就很便宜。 if all it does is capture the current thread id and release, they should all be complete virtually immediately after they fire. 如果所做的只是捕获当前线程ID并释放,则它们实际上应该在启动后立即完成。

The code needs to run somewhere ; 代码需要在某个地方运行; what thread would you expect it to run on? 希望它在哪个线程上运行? Your main thread is currently in a blocking Wait() call, so it can't do anything. 您的主线程当前处于阻塞的Wait()调用中,因此它无法执行任何操作。 Note: you should almost never call .Wait() on an incomplete Task . 注意:您几乎永远不要在不完整的Task上调用.Wait()

After some research, i found the answer i was looking for. 经过一番研究,我找到了想要的答案。 https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/ https://blogs.msdn.microsoft.com/pfxteam/2012/01/20/await-synchronizationcontext-and-console-apps/

Actually Mrinal Kamboj added a comment, to my question, pointing to the right direction. 实际上Mrinal Kamboj在我的问题上添加了一条评论,指出了正确的方向。 I needed to create a synchronization context for the continuations to run on. 我需要为继续运行创建一个同步上下文 This snippet also shows why you should not Wait() on incomplete task: if the continuation runs on the same thread (which happens for GUI threads or ASP.NET) , if this thread is locked waiting for continuation then we run in a dead-lock situation. 此代码段还显示了为什么您不应该对未完成的任务使用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(); }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 将变量传递给异步方法后,如何将主变量中的新实例设置变量影响异步方法? - After passing variable to async method, how will setting variable to new instance in main thread affect async method? 使用 async/await 会创建一个新线程吗? - Does the use of async/await create a new thread? 在新线程上启动方法时使用async关键字是否有效率? - Is it more or less efficient to use the async keyword along with starting the method on a new thread? 异步调用如何知道何时在新线程中开始完成事件或使用调用者线程 - How does async call knows when to start completed event in new thread or use caller thread webapi 2 - 如何在新线程中正确调用长时间运行的方法async /,并将响应返回给客户端 - webapi 2 - how to properly invoke long running method async/in new thread, and return response to client 如何在指定线程中运行异步方法 - How to run async method in specified thread 如何为每个异步操作创建一个新上下文并以线程安全的方式使用它来存储一些数据 - How to create a new context per every async operation and use it in a thread-safe manner for storing some data 是否建议/可以在新线程中运行异步void方法? - Is it recommended/acceptable to run an async void method into a new thread? 在BeginInvoke()中调用异步方法会产生一个&#39;新&#39;线程吗? - Does calling an async method within BeginInvoke() spawn a 'new' thread? 每次调用异步void方法时都会创建一个新线程吗? - Does an async void method create a new thread everytime it is called?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM