C#线程任务不按顺序运行

时间:2017-09-14 16:26:09

标签: c# multithreading

我有一个播放录制流的应用程序,它有一个COM接口,提供启动重播流以及其他控制变量的功能。我想使用按钮单击事件(Visual Studio桌面表单应用程序)完成以下任务(测试用例):

  1. 从0秒(0分钟)播放录音10秒
  2. 然后从60秒(1分钟)开始录制15秒
  3. 最后从120秒(2分钟)开始录制20秒。 (流长约3分钟)
  4. 我正在为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中。

2 个答案:

答案 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));
}