27

1 つのボタンと 1 つのラベルを持つ WinForms アプリケーションに次のコードがあります。

using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            await Run();
        }

        private async Task Run()
        {
            await Task.Run(async () => {
                await File.AppendText("temp.dat").WriteAsync("a");
                label1.Text = "test";
            });    
        }
    }
}

これは、私が取り組んでいる実際のアプリケーションの簡易版です。私は async/await を使用することでプロパティTask.Runを設定できるという印象を受けました。label1.Textただし、このコードを実行すると、UI スレッドにいないというエラーが表示され、コントロールにアクセスできません。

ラベル コントロールにアクセスできないのはなぜですか?

4

6 に答える 6

19

これを試して:

交換

label1.Text = "test";

SetLabel1Text("test");

クラスに以下を追加します。

private void SetLabel1Text(string text)
{
  if (InvokeRequired)
  {
    Invoke((Action<string>)SetLabel1Text, text);
    return;
  }
  label1.Text = text;
}

UI スレッドを使用していない場合、InvokeRequired は true を返します。Invoke() メソッドは、デリゲートとパラメーターを受け取り、UI スレッドに切り替えてから、メソッドを再帰的に呼び出します。メソッドは Invoke() が戻る前に再帰的に呼び出されているため、Invoke() 呼び出しの後に戻ります。メソッドが呼び出されたときに UI スレッド上にいた場合、InvokeRequired は false であり、割り当ては直接実行されます。

于 2014-09-18T02:29:17.053 に答える
16

これを試して

private async Task Run()
{
    await Task.Run(async () => {
       await File.AppendText("temp.dat").WriteAsync("a");
       });
    label1.Text = "test";
}

または

private async Task Run()
{
    await File.AppendText("temp.dat").WriteAsync("a");        
    label1.Text = "test";
}

または

private async Task Run()
{
    var task = Task.Run(async () => {
       await File.AppendText("temp.dat").WriteAsync("a");
       });
    var continuation = task.ContinueWith(antecedent=> label1.Text = "test",TaskScheduler.FromCurrentSynchronizationContext());
    await task;//I think await here is redundant        
}

async/await は、UI スレッドで実行されることを保証しません。タスクが完了するとawait、現在のコンテキストがキャプチャされ、キャプチャされたコンテキストで実行が続行されます。SynchronizationContext

したがって、あなたの場合、await内部にネストされているTask.Runため、2番目はfromによって実行されているため、awaitそうではないコンテキストをキャプチャします。UiSynchronizationContextWorkerThreadThreadPool

これはあなたの質問に答えていますか?

于 2013-09-26T13:00:41.437 に答える
6

なぜ Task.Run を使用するのですか? 新しいワーカースレッド(CPUバウンド)を開始すると、問題が発生します。

あなたはおそらくそれを行うべきです:

    private async Task Run()
    {
        await File.AppendText("temp.dat").WriteAsync("a");
        label1.Text = "test";    
    }

await .ConfigureAwait(false); を使用する場合を除いて、同じコンテキストで続行することを確認してください。

于 2013-09-26T12:36:21.930 に答える
-1

非同期の理解のために私が提供した最新の回答を提供します。

解決策は、ご存知のとおり、非同期メソッドを呼び出すときにタスクとして実行する必要があることです。

これは、参照用に使用できる簡単なコンソール アプリ コードです。これにより、概念を簡単に理解できるようになります。

using System;
using System.Threading;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Starting Send Mail Async Task");
        Task task = new Task(SendMessage);
        task.Start();
        Console.WriteLine("Update Database");
        UpdateDatabase();

        while (true)
        {
            // dummy wait for background send mail.
            if (task.Status == TaskStatus.RanToCompletion)
            {
                break;
            }
        }

    }

    public static async void SendMessage()
    {
        // Calls to TaskOfTResult_MethodAsync
        Task<bool> returnedTaskTResult = MailSenderAsync();
        bool result = await returnedTaskTResult;

        if (result)
        {
            UpdateDatabase();
        }

        Console.WriteLine("Mail Sent!");
    }

    private static void UpdateDatabase()
    {
        for (var i = 1; i < 1000; i++) ;
        Console.WriteLine("Database Updated!");
    }

    private static async Task<bool> MailSenderAsync()
    {
        Console.WriteLine("Send Mail Start.");
        for (var i = 1; i < 1000000000; i++) ;
        return true;
    }
}

ここでは、メール送信というタスクを開始しようとしています。バックグラウンドでメール送信タスクを実行している間に、暫定的にデータベースを更新したいと考えています。

データベースの更新が完了すると、メール送信タスクが完了するのを待っています。ただし、このアプローチでは、タスクをバックグラウンドで実行しながら元の (メイン) スレッドを続行できることは明らかです。

于 2014-09-18T02:33:10.480 に答える