1

DoTheStuff().Wait();コンソール アプリケーションとして実行している場合、次のプログラムがハングします。

namespace Test
{
    using System.Threading.Tasks;
    using System.Windows.Forms;

    class Program
    {
        static void Main(string[] args)
        {
            new Form();
            DoTheStuff().Wait();
        }

        private static async Task DoTheStuff()
        {
            await Task.Delay(1000);
        }
    }
}

ただし、行をコメントアウトすると、期待どおりに機能しますnew Form();。(1 秒間実行してから終了します)。 期待される動作を維持しながら Form インスタンスを保持するにはどうすればよいですか?

さて、興味があれば背景をいくつか:

Windows サービスとして (ローカルでテストするときはコンソールとして) ホストされているアプリケーションがあります。

SystemEvents.TimeChangedイベントにアクセスできる必要があります。

ただし、ドキュメントに従って、これは Windows フォームがある場合にのみ機能します (したがって、サービスまたはコンソール アプリでは機能しません)。回避策は、リンクされたドキュメントに記載されており、非表示のフォームを作成することで構成されています。

残念ながら、プログラムは代わりに完全にフリーズします。これは、await とFormインスタンスを持つことの組み合わせが原因です。

SystemEvents.TimeChangedでは、イベントにアクセスしている間、どうすれば予想される非同期/待機動作を維持できますか?


以下のヘルプのおかげで、フリーズせずに動作する修正されたコードがここにあります。

namespace Test
{
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Forms;

    class Program
    {
        static void Main(string[] args)
        {
            new Thread(() => Application.Run(new Form())).Start();
            // SynchronizationContext.SetSynchronizationContext(null);
            DoTheStuff().Wait();
        }

        private static async Task DoTheStuff()
        {
            await Task.Delay(1000);
        }
    }
}

私のプログラムでは、待機中のタスクにスレッドプールを使用する必要があるため、「SynchronizationContext.SetSynchronizationContext(null);」を使用する必要があります。フォームは明らかに何らかの理由で初期化したため、これは良い習慣ではないと思います。しかし、ユーザー入力なしで非表示のフォームを実行すると (これはサービスです!)、今のところ害は見られません。

MS は例 2 を使用して発生する可能性のある問題についても言及していないため、ドキュメントは少し不完全に感じられます (await/async は、フォームのインスタンス化時に動作を暗黙的に変更します)。

4

2 に答える 2

4

これは仕様によるものです。新しい Form オブジェクトを作成すると、Winforms 配管が新しい SynchronizationContext をインストールします。SynchronizationContext.Current プロパティを見ると、デバッガーで確認できます。

非同期で何かを行うときはいつでも、そのプロパティは重要です。null (デフォルト) の場合、await を使用すると、スレッドプール スレッドで実行するコードが取得されます。そうでない場合、await 配管は SynchronizationContext.Post() メソッドを呼び出して await を実装します。これにより、コードがメイン スレッドで実行されるようになります。

しかし、契約を破ったため、それはあなたのプログラムでは機能しません。Application.Run() を呼び出しませんでした。必須。

SystemEvents クラスは独自の非表示の通知ウィンドウを作成し、提供しない場合はメッセージ ループを生成します。フォームを作成する必要はありません。その結果、イベントはメイン スレッドではなく、任意のスレッドで発生します。そのため、ロック要件に注意してください。

于 2013-04-29T12:05:47.813 に答える