告诉任务等待计时器停止
本文关键字:计时器 等待 任务 | 更新日期: 2023-09-27 18:25:50
解释&背景:如果这个问题很糟糕,我很抱歉,但我正在努力思考任务。我目前有一组类,它们将控制它们的构造函数,并允许我自动进行用户交互(在文本框中键入、单击按钮等)
其中一个例子是我的类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_elaped事件。我希望能够在Task认为"WriteText"完成之前完成事件。
和问题:有没有一种方法可以告诉任务能够考虑到这些类型的事情?我是不是不理解任务?
编辑-我正在使用3.5和3.5实现的TPL
现在您没有从使用任务中获得任何好处。使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
)
这些方法在基于任务的异步模式中有很好的描述