Установите ApartmentState для async void main

У меня есть приложение Windows Forms.

Теперь я хочу использовать метод 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);
}

person Jürgen Steinblock    schedule 29.11.2017    source источник
comment
@Evk - вы получите ThreadStateException, поскольку по определению текущий поток имеет уже запущен.   -  person Damien_The_Unbeliever    schedule 29.11.2017
comment
Да, конечно, это не имело смысла.   -  person Evk    schedule 29.11.2017


Ответы (3)


STA - это тот, в котором вы обещаете, что один поток будет управлять насосом сообщений Windows (обычно скрытым за вызовом _ 1_). Вы запустите перекачку сообщений в своей беседе, когда она ничем не занята.

Метод async - это метод, который, когда ему нечего делать, освобождает свой поток, чтобы уйти и заняться другими делами.

Я не могу согласовать эти две концепции. Если вам нужен STA, вы хотите сохранить эту цепочку и перекачивать сообщения. Так что использование async main здесь не имеет смысла.


В этом конкретном случае (различные шаги инициализации, которые могут выиграть от await, за которым следует Application.Run, я бы использовал async Main без атрибута STAThread. Затем я бы явно создал поток STA специально для последующего запуска цикла сообщений Windows.

Нет правила, согласно которому первым потоком в вашей программе должен быть поток / a STA, и я думаю, что это обеспечивает наиболее четкое разделение. Если у вашего async Main нет дальнейшей полезной работы, вы можете поделиться TaskCompletionSource между Main и потоком, выполняющим цикл сообщений, который затем сигнализирует о завершении после возврата Application.Run().

person Damien_The_Unbeliever    schedule 29.11.2017
comment
Чтобы запустить поток STA, используйте этот код: var thread = new Thread(() => DoSomething()); thread.SetApartmentState(ApartmentState.STA); thread.Start(); - person Maxence; 16.08.2019

STAThread не работает с async main.

Это известная и открытая проблема.

Проблема в том, что код, преобразующий async main в "нормальный" main, не копирует атрибут.

Вот дополнительная информация о нескопированных атрибутах.

В некоторых обсуждениях они ссылаются на использование _ 5_ для идиоматического использования асинхронного кода в потоке STA.

person Community    schedule 29.11.2017
comment
Спасибо, подозревал, что это ошибка, но сам не смог найти рабочий элемент. - person Jürgen Steinblock; 29.11.2017
comment
@ JürgenSteinblock - Я не уверен, что это ошибка сама по себе. Как я уже отмечал, с STA вы обещаете запустить насос сообщений. Все варианты Application.Run по своей природе блокируются до тех пор, пока насосу сообщений не будет сказано о выходе - так что либо вы не вызываете Application.Run() (или моральный эквивалент) и, следовательно, не выполняете обещание STA, либо вы все равно собираетесь заблокировать а не извлекать выгоду из async. - person Damien_The_Unbeliever; 29.11.2017
comment
@ JürgenSteinblock - или, по крайней мере, ошибка в том, что компилятор должен выдать ошибку, если вы попытаетесь пометить async main с помощью STAThread вместо копирования атрибута. - person Damien_The_Unbeliever; 29.11.2017
comment
@Damien_The_Unbeliever Что ж, проблема отмечена как Bug в репозитории dotnet/roslyn, так что, возможно, это как-то решится. Согласен, использование async/await с блокировкой Application.Run() не имеет смысла. Но я использовал его только для кода, выполнявшегося до Run(), и не знал, что это изменит состояние моих приложений. Ошибка компилятора или времени выполнения была бы лучше, чем просто игнорировать атрибут. - person Jürgen Steinblock; 29.11.2017

Я знаю, что это старый, но ответ Дэмиена и последующий комментарий помогли мне с моей проблемой. У меня есть консольное приложение, в котором мне нужно вызвать 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;
}
person Jason W    schedule 03.01.2020
comment
Это почти привело меня к этому, но после того, как все заработало, я получаю COM object that has been separated from its underlying RCW cannot be used исключение. - person Mike Lowery; 11.09.2020
comment
Я раньше не сталкивался с этой ошибкой, но это может помочь (предлагает запускать COM-объект в каждом потоке): stackoverflow.com/questions/1567017/ - person Jason W; 11.09.2020
comment
Это происходило в MS Test при запуске теста в режиме отладки. В противном случае ошибок нет. - person Mike Lowery; 11.09.2020