Set ApartmentState on a Task

2019-01-06 12:08发布

问题:

I am trying to set the apartment state on a task but see no option in doing this. Is there a way to do this using a Task?

for (int i = 0; i < zom.Count; i++)
{
     Task t = Task.Factory.StartNew(zom[i].Process);
     t.Wait();
}

回答1:

When StartNew fails you just do it yourself:

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;
}

(You can create one for Task that will look almost identical, or add overloads for some of the various options that StartNew has.)



回答2:

You could for example make a new task as follows:

       try
        {
            Task reportTask = Task.Factory.StartNew(
                () =>
                {
                    Report report = new Report(this._manager);
                    report.ExporterPDF();
                }
                , CancellationToken.None
                , TaskCreationOptions.None
                , TaskScheduler.FromCurrentSynchronizationContext()
                );

            reportTask.Wait();
        }
        catch (AggregateException ex)
        {
            foreach(var exception in ex.InnerExceptions)
            {
                throw ex.InnerException;
            }
        }


回答3:

An overload of Servy's answer to start a void Task

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


回答4:

This is what I'm using with Action since I don't need to return anything:

public static class TaskUtil
{
    public static Task StartSTATask(Action action)
    {
        var tcs = new TaskCompletionSource<object>();
        var thread = new Thread(() =>
        {
            try
            {
                action();
                tcs.SetResult(new object());
            }
            catch (Exception e)
            {
                tcs.SetException(e);
            }
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        return tcs.Task;
    }
}

Where I call it like this:

TaskUtil.StartSTATask(async () => await RefreshRecords());

For details, please see https://github.com/xunit/xunit/issues/103 and Func vs. Action vs. Predicate

FYI, this is the exception I was getting where I needed to set the apartment state:

System.InvalidOperationException occurred HResult=-2146233079
Message=The calling thread must be STA, because many UI components require this. Source=PresentationCore StackTrace: at System.Windows.Input.InputManager..ctor() at System.Windows.Input.InputManager.GetCurrentInputManagerImpl() at System.Windows.Input.Keyboard.ClearFocus()



标签: c# task