将ApartmentState设置为async void main

时间:2017-11-29 12:59:57

标签: c# async-await sta mta

我有一个Windows窗体应用程序。

现在我想使用async方法。

从C#7.1开始,我可以使用async Main方法:
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-1

但是,现在我的STAThread属性被忽略,我的应用程序在MTA中运行。这是设计还是我可以强制我的应用程序再次以STA模式运行?

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static async Task Main(string[] args)
{
    // returns MTA
    Console.WriteLine("{0}", Thread.CurrentThread.ApartmentState);
}

3 个答案:

答案 0 :(得分:3)

STA是您承诺单个线程将运行Windows消息泵(通常隐藏在Application.Run()调用之后)的版本。当你的线程没有被占用时,你将在你的线程上运行消息泵。

async方法是一种方法,当它没有更好的事情要做时,会释放它的线程以便做其他事情。

我无法将这两个概念对齐。如果你想要STA,你想要保持该线程和泵消息。所以使用async main在那里没有意义。

在这种特殊情况下(各种初始化步骤可能会受益await后跟Application.Run我会使用async Main 而不使用 {{1}然后我将显式创建一个STA线程,专门用于运行windows消息循环。

没有规则你的程序中的第一个线程必须是/一个STA线程,我认为这提供了最干净的分离。如果您的STAThread没有其他有用的工作要做,您可能希望在async Main和消息循环运行线程之间共享TaskCompletionSource,然后在Main返回后发出完成信号

答案 1 :(得分:2)

Application.Run()不适用于STAThread

这是known and open issue

问题在于,将async main转换为“普通”async main的代码不会复制该属性。

以下是more information具体关于未复制的属性。

some of the discussion中,他们指的是使用SingleThreadedSynchronizationContext来允许在STA线程中使用异步代码。

答案 2 :(得分:2)

我知道这很老了,但是达米安(Damien)的回答和随后的评论对我的问题有所帮助。我有一个控制台应用程序,其中需要调用async方法,在某些时候可能需要STA执行才能使用OpenFileDialog

这是我得到的代码,以防它对他人(或对我未来的自我有帮助)。

1。创建用于将线程作为STA运行的扩展方法

public static class Extensions
{
    public static void RunSTA(this Thread thread)
    {
        thread.SetApartmentState(ApartmentState.STA); // Configure for STA
        thread.Start(); // Start running STA thread for action
        thread.Join(); // Sync back to running thread
    }
}

2。使用async main到应用程序方法(没有await属性)创建了[STAThread]方法。

class Program
{
    static async Task Main(string[] args)
    {
        await App.Get().Run(args);
    }
}

3。使用扩展方法将OpenFileDialog呼叫与STA打包

public string[] GetFilesFromDialog(string filter, bool? restoreDirectory = true, bool? allowMultiSelect = true)
{
    var results = new string[] { };
    new Thread(() =>
    {
        using (var dialog = new OpenFileDialog())
        {
            dialog.Filter = filter;
            dialog.RestoreDirectory = restoreDirectory ?? true;
            dialog.Multiselect = allowMultiSelect ?? true;
            if (dialog.ShowDialog() != DialogResult.OK)
                return; // Nothing selected
            results = dialog.FileNames;
        }
    }).RunSTA();
    return results;
}