Reputation: 1147
I have an application which plays a recording stream, it has a COM Interface to provide functionality to start replay stream along with other control variables. I want to accomplish the following task (test case) using an button click event (Visual Studio Desktop Form Application):
I am creating thread for each of the above task from 1-3 and then I am using join method to wait for each task to be completed and then start the next thread.
My code:
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 is just an instance of an class that uses COM Interface, in a nutshell this is what startReplayAt3 method looks like
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;
}
However, on the actual interface I am not seeing the desired results, it performs task 1, then does something with task 2 but skip the replay part entirely, then performs task 3. Next time when I click the button, it skips task 1, goes straight to task 2 and ends without performing task 3. So as you can see it is very unpredictable and strange behavior.
Why could be the cause, I am making sure that my thread are in sync but it doesn't seem to reflect in the UI of the application I am running.
Upvotes: 0
Views: 324
Reputation: 456757
From your description of the program's behavior, it seems to me that startReplayAt3
in fact only starts the replay.
What you want to do is start the replay, then wait until it finishes, and then start the next replay.
If the COM object gives you an event that is raised when the replay completes, then you're good; you should wrap that event into a task, and use await
to consume it, something like this:
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;
}
used as such:
private async void btnReplayForward_Click(object sender, EventArgs e)
{
await mpd.ReplayAsync(0, 10);
await mpd.ReplayAsync(60, 15);
await mpd.ReplayAsync(120, 20);
}
But some COM objects don't let you know when they're done (notably, a lot of media players are really bad at this). In that case, you'll either have to poll some kind of IsPlaying
flag on the COM object, or just use a timeout like this:
public static Task ReplayAsync(this MPD mpd, int start, int duration)
{
mpd.startReplayAt3(start, duration);
return Task.Delay(TimeSpan.FromSeconds(duration));
}
Upvotes: 2
Reputation: 189
I'm not entirely sure about the issue you're seeing with the threads :\. The implementation seems a little off, the .Abort() calls seem redundant since the thread will be completed by the time you're calling the .Abort().
Why not use asynchronous functionality?
await Task.Run(() => mpd.startReplayAt3(0, 10));
await Task.Run(() => mpd.startReplayAt3(60, 15));
await Task.Run(() => mpd.startReplayAt3(120, 20));
or:
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());
or:
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);
Like yourself I used to create many threads for executing tasks simultaneously, but I have fallen in love with async/await.
Make sure to change your _Click method to an asynchronous method using the async keyword.
Upvotes: 2