我有一个播放录制流的应用程序,它有一个COM接口,提供启动重播流以及其他控制变量的功能。我想使用按钮单击事件(Visual Studio桌面表单应用程序)完成以下任务(测试用例):
我正在为1-3中的每个任务创建线程,然后我使用join方法等待每个任务完成,然后启动下一个线程。
我的代码:
private void btnReplayForward_Click(object sender, EventArgs e) {
Thread replayAtThread = new Thread(() => mpd.startReplayAt3(0, 10));
replayAtThread.Start();
replayAtThread.Join();
Thread replayAtThread1 = new Thread(() => mpd.startReplayAt3(60, 15));
replayAtThread1.Start();
replayAtThread1.Join();
Thread replayAtThread2 = new Thread(() => mpd.startReplayAt3(120, 20));
replayAtThread2.Start();
replayAtThread2.Join();
replayAtThread.Abort();
replayAtThread1.Abort();
replayAtThread2.Abort();
}
mpd只是一个使用COM接口的类的实例,简而言之这就是startReplayAt3方法的样子
public bool startReplayAt3(double start, double duration)
{
//Set start and duration
//Start replay at start and duration values
bool isReplay = //get replay value, true if replay running, false if stopped
while (isReplay)
isReplay = //get replay value, true if replay running, false if stopped
return isReplay;
}
然而,在实际界面上我没有看到期望的结果,它执行任务1,然后执行任务2但完全跳过重放部分,然后执行任务3.下次单击按钮时,它会跳过任务1,直接进入任务2并结束而不执行任务3.所以你可以看到它是非常不可预测和奇怪的行为。
为什么可能是原因,我确保我的线程同步,但它似乎并没有反映在我正在运行的应用程序的UI中。
答案 0 :(得分:2)
我不完全确定您在线程中遇到的问题:\。实现似乎有些偏差,.Abort()调用似乎是多余的,因为线程将在您调用.Abort()时完成。
为什么不使用异步功能?
await Task.Run(() => mpd.startReplayAt3(0, 10));
await Task.Run(() => mpd.startReplayAt3(60, 15));
await Task.Run(() => mpd.startReplayAt3(120, 20));
或:
var replayTasks = new List<Task>();
replayTasks.Add(Task.Run(() => mpd.startReplayAt3(0, 10)));
replayTasks.Add(Task.Run(() => mpd.startReplayAt3(60, 15)));
replayTasks.Add(Task.Run(() => mpd.startReplayAt3(120, 20)));
Task.WaitAll(replayTasks.ToArray());
或:
var replayTasks = new Task[] {
Task.Run(() => mpd.startReplayAt3(0, 10)),
Task.Run(() => mpd.startReplayAt3(60, 15)),
Task.Run(() => mpd.startReplayAt3(120, 20))
};
Task.WaitAll(replayTasks);
像我一样,我曾经创建过很多线程来同时执行任务,但我已经爱上了异步/等待。
确保使用async关键字将_Click方法更改为异步方法。
答案 1 :(得分:2)
根据您对该计划行为的描述,在我看来startReplayAt3
实际上只有开始重播。
你要做的是开始重播,然后等到它完成,然后开始下一次重播。
如果COM对象为您提供了在重播完成时引发的事件,那么您就是好的;您应Click to see,并使用await
来使用它,如下所示:
public static Task ReplayAsync(this MPD mpd, int start, int duration)
{
var tcs = new TaskCompletionSource();
ReplayFinishedHandler handler = null;
handler = (s, e) => { tcs.TrySetCompleted(); mpd.ReplayFinished -= handler; };
mpd.ReplayFinished += handler;
mpd.startReplayAt3(start, duration);
return tcs.Task;
}
用作:
private async void btnReplayForward_Click(object sender, EventArgs e)
{
await mpd.ReplayAsync(0, 10);
await mpd.ReplayAsync(60, 15);
await mpd.ReplayAsync(120, 20);
}
但是有些COM对象不会让你知道他们什么时候完成(特别是很多媒体播放器真的很糟糕)。在这种情况下,您必须在COM对象上轮询某种IsPlaying
标志,或者只使用这样的超时:
public static Task ReplayAsync(this MPD mpd, int start, int duration)
{
mpd.startReplayAt3(start, duration);
return Task.Delay(TimeSpan.FromSeconds(duration));
}