How to set ApartmentState.STA when using Task

T.Zacks 3,996 Reputation points
2022-02-15T08:22:16.403+00:00

this way i am running code under thread.

await Task.Run(() =>
{
DoTask();
});

when i try to use this code then getting compilation error.

await Task.Run(() =>
{
     DoTask();
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());

I curious to know how could i set ApartmentState.STA when using TASK?

also tell me what is the meaning of ApartmentState.STA ? if i set ApartmentState.STA then what will happen ?

i saw that if i work with thread class then we can set ApartmentState.STA

public static Task<T> StartSTATask<T>(Func<T> func)
{
    var tcs = new TaskCompletionSource<T>();
    Thread thread = new Thread(() =>
    {
        try
        {
            tcs.SetResult(func());
        }
        catch (Exception e)
        {
            tcs.SetException(e);
        }
    });
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
    return tcs.Task;
}

please suggest a work around for Task.Run() method. thanks

Developer technologies | C#
0 comments No comments
{count} votes

Accepted answer
  1. Karen Payne MVP 35,586 Reputation points Volunteer Moderator
    2022-02-15T10:31:46.093+00:00

    Please ask one question at a time.

    Read the docs on Task.Run.

    private async Task Whatever(CancellationToken cancellationToken = default)  
    {  
        await Task.Run(async () =>  
        {  
            await DoTask();  
        }, cancellationToken);  
    }  
      
    private static async  Task<bool> DoTask()  
    {  
        await Task.Delay(0);  
        return true; // dummy result  
    }  
    

    And for the record, this is what you are attempting

    • Task.Run "should simply be thought of as a quick way to use Task.Factory.StartNew without needing to specify a bunch of parameters. It’s a shortcut"
    • Task.Run(someAction); is exactly equivalent to:

    Code

    private async Task Whatever()  
    {  
        await Task.Factory.StartNew  
        (DoTask()  
            , CancellationToken.None  
            , TaskCreationOptions.None  
            , TaskScheduler.FromCurrentSynchronizationContext()  
        );  
    }  
      
    public static Action DoTask()  
    {  
        throw new NotImplementedException();  
    }  
    
    2 people found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Bruce (SqlWork.com) 78,006 Reputation points Volunteer Moderator
    2022-02-16T17:59:21.067+00:00

    ApartmentState is part of com and used to manage thread access to method calls. an STA application only allows one thread in the apartment at a time. The idea is a lock on the apartment is taken, and only one thread allowed inside at a time. This was typically the main thread, and message loops are used.

    anyway once in the apartment, if the thread call a routine in another module, it requires that module to follow the STA rules. the flag is used for this.

    Task.Run and await are not compatible with STA apartments, because it returns on a new thread.

    //thread 1
    await SomeAsyncMethod();
    // now the pool thread of the async method

    if you want to use awaitable, in a STA thread, try need to create a new thread, that calls the Task.Run(). and use a different sync method on the calling thread. Probably more work than its worth. also as you are stalling the main thread, there is no point in the new thread.

    also apartment thread calls are supposed to be quick. if you need a long process it should be a new thread, that called from message loop via polling, a semaphores or a mutex.

    SynchronizationContext is part of the .net Task library, and is way to pass context information to a Task. This allows thread agility (task continuations or callbacks can be on a different thread), and context information passed to each continuation.

    In traditional async handling the callbacks are on the same thread, so ThreadLocalStorage can be used for context. But with the async Task library (which use pool threads and a queue , callbacks are typically on a different thread). Thus ThreadLocalStorage does not work. So when the task scheduler starts a thread from the pool for new Task or Task continuation, the context is set. You can gain performance by disabling the context.

    1 person found this answer helpful.

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.