none
Task and Parallel.Invoke in a library, is it a bad idea to use it? RRS feed

  • Question

  • I have read that to do a fake async method in this way it is a bad idea:

    public int myMethodSyn()
    {
        //sync operations
    
        return result;
    }
    
    public async int myMethodAsync()
    {
        return await Task.Run(myMethodSync);
    }

    One of the reasons that I have read it is because for example, ASP can have scalability problems with this kind of libraries because tasks use the thread pool and ASP need the thread pool to attend each call. So the library can consume all the threads of the thread pool al block ASP. SO it is better allow to the client decides how to use the thread pool.

    If am not wrong, Parallel.Invoke use the thread pool too to run methods in parallel, so I guess that if I use a method in my library that uses parallel.Invoke, or parallel.Foreach or any of this ways to run code in parallel, I would have the same problem. Is it true?

    My idea is to run two mthods in parallel because they are indepent and I could get a better performance if I run them in parallel. So I would have somthing like that:

    public int myMainMethodSync()
    {
        int result01 = myMethod01Sync();
        int result02 = myMethod02Sync();
        return result01 + result02;
    }
    
    private void myMethod01Sync()
    {
    }
    
    private void myMethod02Sync()
    {
    }
    
    public int myMainMethodAsync()
    {
        Task myTsk01 = Task.Run(myMethod01Sync);
        Task myTsk02 = Task.Run(myMethod02Sync);
        Task.WhenAll(myTsk01, myTsk02);
    
        return myTsk01.Result + myTsk02.Result;
    }
    
    public int Task myMainMethodParallel()
    {
        int result01;
        int result02;
        Parallel.Invoke(() => result01 = myMethod01Sync(),
                        () => result02 = myMethod02Sync());
    
        return result01 + result02;
    }

    The idea is it to have a sync method that run the two methods in sync. So the client who use the library knows that the method will not use thread pool.

    Later I have two options to run the methods at the same time, with tasks or with parallel.Invoke.

    In the case of the tasks, I am using a fake async methods because I am wraping the sync method inside a task, that use two threads from the threadpool. If I am not wrong, this is not recommended.

    The other option it is to use Parallel.Invoke, that uses threads from thread pool too, so I guess it has the same problem that with tasks, so I guess that it is not recommended too.

    In my case I would prefer to use task, because I can decide with a condition when to run the method02Sync for example, according to some condiciotion, so I could save the cost to assign a thread to run the second method if I know that it is not needed in some cases. I guess in parallel.Invoke this is not possible.

    However, I think that in this case, how I implement a sync method too, I let the client to choose the method that it considerates better in its case, so really it is a bad option to use tasks in the async method?

    If both solutions are bad, tasks and Parallel.Invloke, then it is not recommended to run parallel code in libraries and only use it in the top level, in the UI or client of the library? Because I guess that in this case the use of parallel is very restrictive, because in the top level, in the UI, it is not possible to use parallel if it decides it is possible because tell to library use threads or not, because it wouldn't have parallel methods.

    In sumary, is my solution, expose sync and async methods a bad idea? is it bad idea to use task or parallel code in the libraries? If one of them it is better option, which one?

    Thanks.

    • Changed type ComptonAlvaro Thursday, November 16, 2017 10:11 AM
    Monday, November 13, 2017 9:14 AM

Answers

  • Seems like a duplicate post of the question you asked already.

    Exposing both sync and async is generally not a good idea. If you have legacy sync code then leave it as is. For newer code consider exposing just an async version. It cuts down on the code you write. If the caller wants a sync version they can always call .Wait or .Result. Only the caller knows the context to use.

    You can use Task and parallel in your library code. There are no restrictions. Consider marking your asyn calls as ConfigureAwait(false) though so you don't accidentally cause the calling code to freeze (aka in a UI). Ultimately the caller is responsible for determining what the final task is continued on but your intermediate calls should always use ConfigureAwait.

    I really think you're putting too much thought into this. Just use Task and async/await in your library code (with ConfigureAwait(false)) if you need async behavior. Forget about sync versions unless they make sense to have in your library code. If you need to do several things in parallel then use Parallel or multiple async calls with a single wait at the end. It really doesn't matter whether this occurs in library code or not. Provided you're using ConfigureAwait it'll just work.


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by ComptonAlvaro Thursday, November 16, 2017 10:11 AM
    Monday, November 13, 2017 4:03 PM
    Moderator

All replies

  • Seems like a duplicate post of the question you asked already.

    Exposing both sync and async is generally not a good idea. If you have legacy sync code then leave it as is. For newer code consider exposing just an async version. It cuts down on the code you write. If the caller wants a sync version they can always call .Wait or .Result. Only the caller knows the context to use.

    You can use Task and parallel in your library code. There are no restrictions. Consider marking your asyn calls as ConfigureAwait(false) though so you don't accidentally cause the calling code to freeze (aka in a UI). Ultimately the caller is responsible for determining what the final task is continued on but your intermediate calls should always use ConfigureAwait.

    I really think you're putting too much thought into this. Just use Task and async/await in your library code (with ConfigureAwait(false)) if you need async behavior. Forget about sync versions unless they make sense to have in your library code. If you need to do several things in parallel then use Parallel or multiple async calls with a single wait at the end. It really doesn't matter whether this occurs in library code or not. Provided you're using ConfigureAwait it'll just work.


    Michael Taylor http://www.michaeltaylorp3.net

    • Marked as answer by ComptonAlvaro Thursday, November 16, 2017 10:11 AM
    Monday, November 13, 2017 4:03 PM
    Moderator
  • Thanks so much for your tips.

    But I still have doubt. If I only expose the async method, if an ASP application needs to use the library, it only can use the async method which will use threads, and this can be a problem in ASP for scalability reasons. So it is better that the ASP application manages the threads, so isn't it better to expose a sync method for this cases so this methods doesn't consume threads?

    Thanks.

    Monday, November 13, 2017 7:38 PM
  • You should really post this question on the ASP.NET forums. Using tasks will actually allow your server to handle more requests at one time. When a request come into the server it gets pulled from a request thread pool allocated by the server. If you run out of threads then the server will stop responding to requests until one is freed up. If you have long running requests then scalability plummets.

    To work around this you should ALWAYS use async in ASP.NET. Every controller and page method should be marked async and return a Task. Since the rule is "async all the way" all the code the original request calls will also be async. By doing this, the request thread is released back to the pool which allows the server to handle more requests at the same time as none of the actual work is running on the (limited) request thread pool. As async calls complete, the server will grab an available request thread to finish the request out. ASP.NET is the most perfect use case for using async in the UI.

    Note that with ASP.NET it really isn't necessary to use ConfigureAwait because the runtime doesn't guarantee that the request will be finished on the original thread. But it is considered good practice to use it anyway. The only time you might run into issues is if you're using STA COM objects. For that there is an app settings you can add to allow ASP.NET to work with tasks and COM objects. The ASP.NET forum folks can better explain how all this works.


    Michael Taylor http://www.michaeltaylorp3.net

    Monday, November 13, 2017 10:27 PM
    Moderator