等待计时器停止后告诉任务继续执行

3

解释与背景: 如果这个问题不好,请见谅,但是我正在尝试理解任务。我目前有一组类,它们将控制权传递给它们的构造函数,并允许我自动化用户交互(在文本框中输入,点击按钮等)。

其中一个示例是我的名为TextboxTester的类。以下是我如何引入文本框的代码片段:

public class TextboxTester
{
    private int _currentTextLength = 0;
    private string _text;
    private TextBox _textBox;
    private Timer timer;

    #region Constructor
    public TextboxTester(TextBox TextBox)
    {
        _textBox = TextBox;
    }
    #endregion

我希望能够将动作(任务)链接在一起,以便按顺序依次执行,从而自动化一系列用户输入事件。我查阅了相关资料,了解到了TPL任务,并决定试一试。

我创建了我的TextboxTester类和我的ButtonTester类,并传入了控件。我希望能够在文本框中键入文本,然后点击按钮-因此我调用了这个:

Task.Factory.StartNew(() => txtTester.WriteText("Hello World")).Wait();
Task.Factory.StartNew(() => btnTester.Click(1));

根据我对Task.Factory调用的了解,这是我想要做的——执行第一个动作,等待它完成,然后执行下一个动作。问题在于,TextboxTester.WriteText()使用计时器模拟在TextBox中输入(每秒钟1个字符):

public void WriteText(string Text)
    {
        if (timer == null)
        {
            State.Working = true;
            timer = new Timer();

            try
            {
                _text = Text;
                timer.Elapsed += new ElapsedEventHandler(timer_ElapsedWrite);
                timer.Interval = 1000;
                timer.Enabled = true;
                timer.Start();
            }
            catch
            {
                MessageBox.Show("WriteText timer could not be started.");
            }
        }
    }
void timer_ElapsedWrite(object sender, ElapsedEventArgs e)
    {
        _textBox.Dispatcher.BeginInvoke(new Action(() =>
        {
            TextBoxAutomationPeer peer = new TextBoxAutomationPeer(_textBox);
            IValueProvider valueProvider = peer.GetPattern(PatternInterface.Value) as IValueProvider;
            valueProvider.SetValue(_text.Substring(0, _currentTextLength));

            if (_currentTextLength == _text.Length)
            {
                timer.Stop();
                State.Working = false;
                timer = null;
                return;
            }

            _currentTextLength++;

        }));
    }

**在经过的事件中,_textBox.Dispatcher 调用是为了防止出现“线程已拥有此对象”消息。

最终问题: Task.Factory.StartNew() 调用似乎没有考虑到仍在发生的 time_elapsed 事件。我希望能在任务认为“WriteText”完成之前完成事件。

问题是:是否有一种方法告诉任务能够考虑到这些事情?我是否没有理解任务?

编辑 - 我正在使用3.5和 TPL 的 3.5 实现

1个回答

2

目前您使用任务没有任何好处。将WriteText方法转换为异步方法可以使事情变得更加容易:

public async Task WriteText(string text)
{
    TextBoxAutomationPeer peer = new TextBoxAutomationPeer(_textBox);
    IValueProvider valueProvider = peer.GetPattern(PatternInterface.Value) as IValueProvider;
    for(int i = 1; i < text.Length; ++i)
    {
        await Task.Delay(1000); // no need for timer
        valueProvider.SetValue(text.Substring(0, i));
    }
}

所以你可以编写:

这样,您可以编写

async void TestMethod()
{
   await txtTester.WriteText("Hello World"));
   Task.Factory.StartNew(() => btnTester.Click(1)); // probably sould be refactored too
}

编辑

没有使用async/await的版本,对现有代码进行最小更改:

public class TextboxTester
{
    /* unchanged code */

    // unfortunately, there is no non-generic version of this class
    // basically it allows us to 'manually' complete Task
    private TaskCompletionSource<object> _tcs;

    public Task WriteText(string Text)
    {
        if (timer == null)
        {
            _tcs = new TaskCompletionSource<object>();
            /* unchanged code */
        }
        return _tcs.Task; // return task which tracks work completion
    }

    /* unchanged code */
            if (_currentTextLength == _text.Length)
            {
                timer.Stop();
                State.Working = false;
                timer = null;
                _tcs.TrySetResult(null); // notify completion
                return;
            }
    /* unchanged code */
}

现在您可以编写代码。
// well, you could simply make this a blocking method instead
txtTester.WriteText("Hello World").Wait();
btnTester.Click(1);

或者更好
txtTester.WriteText("Hello World")
         .ContinueWith(t => btnTester.Click(1)); // does not block calling thread

主要思想是设计异步方法,使其能够报告完成情况。有三种常见的模式:
  • Begin/End 方法对(如BeginWriteText返回IAsyncResult,而EndWriteText接受IAsyncResult
  • 在工作完成时触发的事件(public event EventHandler WriteTextCompleted
  • 基于任务的方法(方法返回Task
这些方法在基于任务的异步模式中有很好的描述。

我应该指出我在一个3.5项目中,使用了3.5重新实现的TPL(响应式扩展)。 - Logan B. Lehman
工作正常,谢谢!但由于调度程序而阻塞了UI线程。下一步需要解决这个问题!谢谢! - Logan B. Lehman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接