4

[編集]投稿全体を言い換えて簡略化[/編集]

このブログでは、SynchronizationContext オブジェクトを使用して UI スレッドで Task を実行する例として、次の (少し簡略化しました) を示します。

Task.Factory.StartNew(() =>"Hello World").ContinueWith(
            task => textBox1.Text = task.Result,
            TaskScheduler.FromCurrentSynchronizationContext());

新しいプロジェクトでこれらの結果を繰り返し、UI を安全に更新することはできますが、現在のプロジェクトでは何らかの理由で (それが機能してたとしても) できません。標準の「間違ったスレッドから UI を更新することはできません」という例外が発生します。

私のコード(MainForm_Load(...)内)は次のようなもので、メインフォームにtextBox1が追加された新しいプロジェクトで機能しますが、現在のプロジェクトでは機能しません:

var one = Task.Factory.StartNew(
        () => "Hello, my name is Inigo Montoya");
var two = one.ContinueWith(
        task => textBox1.Text = one.Result,
        TaskScheduler.FromCurrentSynchronizationContext());

誰でも何が起こっているのかについて考えを持っています。

[編集]

フォームを使用してユーザーにログイン情報を求めるオブジェクトのインスタンス化にまでさかのぼってエラーをたどりました。エラーは、フォームが表示されている場合にのみ発生します。(そのフォームが発生する前にハードコードされた値を返すとShow、すべてが正常に機能します)。

新しい質問: 独自のコンストラクターが表示される前に別のフォームを表示する場合、作成中のフォームの SynchronizationContext を取得するにはどうすればよいですか? 何が起こっているかを再現する方法は次のとおりです。

1) 2 つのフォームを作成しTextBoxます。Button

2) クラスを作成するOwnedBy1Uses2

Form1:

public partial class Form1 : Form
{
    OwnedBy1Uses2 member;
    public Form1()
    {
        InitializeComponent();
        member = new OwnedBy1Uses2();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        var ui = TaskScheduler.FromCurrentSynchronizationContext();
        Task<string> getData = Task.Factory.StartNew(
            () => "My name is Inigo Montoya...");
        Task displayData = getData.ContinueWith(
            t => textBox1.Text = t.Result, ui);
    }
}

Form2:

public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();
        DialogResult = System.Windows.Forms.DialogResult.Cancel;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        DialogResult = System.Windows.Forms.DialogResult.OK;
        Hide();
    }
}

OwnedBy1Uses2:

class OwnedBy1Uses2
{
    int x;
    public OwnedBy1Uses2()
    {
        using (Form2 form = new Form2())
        {
            if (form.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                x = 1;
            }
            else
            {
                x = 2;
            }
        }
    }
}
4

1 に答える 1

5

メインスレッドにいるだけでは十分ではありません。有効な が必要ですSynchronizationContext.Current(行にブレークポイントを設定し、FromCurrentSynchronizationContextの値を調べますSynchronizationContext.Current。 の場合nullは、何かが間違っています)。

最もクリーンな修正は、UI メッセージ ループ内 (つまり、WinForms やWPFなどFromCurrentSynchronizationContext) からタスク コードを実行することです。Form.LoadWindow.Loaded

編集:

WinForms には、それを入れるだけでForm.Loadは不十分なバグがありました。実際には、Handleプロパティを読み取って Win32 ハンドルの作成を強制する必要がありました。このバグは修正されたという印象を受けましたが、間違っている可能性があります。

編集2(コメントからコピー):

あなたの問題は、あなたがShowDialogの外で電話していることだと思いますApplication.RunShowDialogネストされたメッセージ ループですが、この場合、親メッセージ ループはありません。ウォッチをオンにしてSynchronizationContext.Currentをステップ実行すると、ダイアログが表示される前はShowDialogであることがわかりますがWindowsFormsSynchronizationContext、ダイアログが表示された後は非 WinForms に変わりますSynchronizationContext。メンバーの作成 (を含むShowDialog) をLoadイベントに移動すると、問題が修正されます。

于 2010-12-22T21:16:23.130 に答える