为 async void main 设置 ApartmentState

Jür*_*ock 8 c# mta sta async-await

我有一个 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);
}
Run Code Online (Sandbox Code Playgroud)

Dam*_*ver 5

STA中,您承诺单个线程将操作 Windows 消息泵(通常隐藏在对 的调用后面Application.Run())。当您的线程未被占用时,您将在线程上运行消息泵。

一种async方法是当它无事可做时,将释放其线程以执行其他操作。

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


在这种特定情况下(即能受益于各种初始化步骤await之后是Application.Run我会用async Main 没有STAThread属性,那么我会明确创建一个STA线程专门用于然后运行Windows消息循环。

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

  • 要启动 STA 线程,请使用以下代码: `var thread = new Thread(() =&gt; DoSomething()); thread.SetApartmentState(ApartmentState.STA); 线程.Start();` (3认同)

小智 5

STAThread不适用于async main.

这是一个已知且悬而未决的问题

问题是转换async main为“正常”的代码main不会复制该属性。

以下是有关未复制属性的更多信息。

一些讨论中,他们提到使用 aSingleThreadedSynchronizationContext来允许在 STA 线程中惯用地使用异步代码。

  • @JürgenSteinblock - 或者,至少,错误是,如果您尝试使用“STAThread”标记“async main”而不是复制属性,则编译器应该引发错误。 (4认同)
  • @Damien_The_Unknowner 好吧,这个问题在“dotnet/roslyn”存储库中被标记为“Bug”,所以也许这会以某种方式得到解决。我同意,将“async/await”与阻塞“Application.Run()”一起使用是没有意义的。但我只将它用于“Run()”之前执行的代码,并且我不知道这会改变我的应用程序状态。编译器或运行时错误比仅仅忽略该属性要好。 (2认同)

Jas*_*n W 4

我知道这已经过时了,但达米安的回答和随后的评论帮助我解决了我的问题。我有一个控制台应用程序,我需要在其中调用一些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
    }
}
Run Code Online (Sandbox Code Playgroud)

2.使用应用程序方法创建async main方法(无属性)。await[STAThread]

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

3. 使用扩展方法来包装OpenFileDialogSTA 的调用

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;
}
Run Code Online (Sandbox Code Playgroud)