2

テキストボックス付きのWindowsフォームがありますtxtOutput。内容があります。txtOutput.Text同じスレッド内から、および次のようにスレッド間で両方を取得および設定するためのプロパティを作成しました。

public string OutputString
{
    get
    {
        string text = string.Empty;
        if (txtOutput.InvokeRequired)
        {
            txtOutput.BeginInvoke(new MethodInvoker(delegate
                {
                    text = txtOutput.Text;
                }));
        }
        else
        {
            text = txtOutput.Text;
        }
        return text;
    }
    set
    {
        if (txtOutput.InvokeRequired)
        {
            txtOutput.BeginInvoke(new MethodInvoker(delegate
                {
                    txtOutput.Text = value;
                }));
        }
        else
        {
            txtOutput.Text = value;
        }
    }
}

同じスレッドからプロパティを設定/取得した場合、次のような関数を呼び出したときの動作は期待どおりPrintMessage()です。

public void PrintMessage()
{
    MessageBox.Show(OutputString);
}

しかし、私がこのように呼ぶときnew Thread(PrintMessage).Start()。はテキストボックスのget値を取得しません(つまり、MessageBoxは空の文字列を表示します)。行にブレークポイントを保持して同じことを行うと、次のようになります。

txtOutput.BeginInvoke(new MethodInvoker(delegate
{
    text = txtOutput.Text;
}));

デバッグ中に、値が取得されます(つまり、がコンテンツMessageBoxを表示しtxtOutputます)

私はsleepどこかにすべきですか?どこで間違いを犯しているのですか?

4

2 に答える 2

2

問題は、UI スレッドがディスパッチャで配置した要求を処理する前に、text 変数への参照を使用して MessageBox.Show() を呼び出していることです。厄介な副作用が発生する可能性があるため、 Thread.Sleep() の使用は避けます。理想的には、コードをリファクタリングして、より非同期的なソリューションを優先して、同期的なプロパティを取り除く必要があります。以下のコードのようなもので、探している結果が得られるはずです。

public void PrintMessage(Action<string> displayAction)
{
    string text = string.Empty;
    if (txtOutput.InvokeRequired)
    {
        txtOutput.BeginInvoke(new MethodInvoker(delegate
        {
            displayAction.Invoke(txtOutput.Text);
        }));
    }
    else
    {
        displayAction.Invoke(txtOutput.Text);
    }
}

そしてそれを呼び出します:

// Get the text asynchronously
PrintMessage(s => MessageBox.Show(s));
于 2012-05-27T02:56:58.060 に答える
0

タスクは、TaskScheduler フォーム UI スレッドで使用できます。ディスパッチャ フォームの UI スレッドをタスク ファクトリに渡すと、このタスクは UI スレッドで実行され、このタスクが作成されたスレッドに結果が返されます。

namespace WpfTest {
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

public partial class MainWindow : Window {
    private readonly TaskScheduler _taskScheduler;

    public MainWindow() {
        InitializeComponent();

        _taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
        button.Click += ButtonOnClick;
    }

    public string Text {
        get {
            if (text.CheckAccess()) {
                return text.Text;
            }
            return Task.Factory.StartNew(
                () => text.Text, CancellationToken.None, TaskCreationOptions.None, _taskScheduler).Result;
        }
        set {
            if (text.CheckAccess()) {
                text.Text = value;
            } else {
                Task.Factory.StartNew(
                    () => { text.Text = value; }, CancellationToken.None, TaskCreationOptions.None, _taskScheduler);
            }
        }
    }

    private void ButtonOnClick(object sender, RoutedEventArgs routedEventArgs) {
        Text += "Test1";
        new Thread(() => { Text += "Test2"; }).Start();
    }
}
}
于 2012-05-27T09:14:04.057 に答える