9

我有一个 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);
}
4

3 回答 3

7

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

一种async方法是,当它没有更好的事情可做时,它会释放它的线程去执行其他事情。

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


在这种特殊情况下(可能受益于的各种初始化步骤,await然后Application.Run我将使用async Main 不带STAThread属性的 。然后,我将明确创建一个 STA 线程,专门用于然后运行 ​​Windows 消息循环。

没有规定程序中的第一个线程必须是/a STA 线程,我认为这提供了最干净的分离。如果您async Main没有其他有用的工作要做,您可能希望共享一个TaskCompletionSourcebetweenMain和 message-loop-running 线程,该线程一旦Application.Run()返回就会发出完成信号。

于 2017-11-29T13:04:30.197 回答
3

STAThread不适用于async main.

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

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

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

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

于 2017-11-29T13:04:30.663 回答
3

我知道这是旧的,但达米安的回答和随后的评论帮助我解决了我的问题。我有一个控制台应用程序,我需要在其中调用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;
}
于 2020-01-03T17:00:56.820 回答