基本线程问题

3

我看到了类似的问题,但没有一个似乎回答了我的问题。

我试图理解线程,但只是使用C#来探索它们。

因此,我在我的主函数中有这样的代码:

var sp = new SongPlayer();
sp.Play(path);

而Play功能看起来像这样:
public void Play(string path)
{
    if (path.EndsWith("mp3"))
        songThread = new System.Threading.Thread(() => PlayMp3(path));
    else if (path.EndsWith("wav"))
        songThread = new System.Threading.Thread(() => PlayWav(path));

    songThread.Start();
    songThread.Join();
}

(songThread是System.Threading.Thread类型)

有人能向我解释一下背后到底发生了什么吗?

如果我添加一个断点并逐步执行,当我执行songThread.Join()时,mp3开始播放。我猜想它不会在调用Start()后立即开始播放,因为控制流仍在主线程中。如果我没有Join语句,而是使用Thread.Sleep(),那么子线程也会执行,但我的问题是关于Join的。它说Join阻止调用线程直到当前线程终止。但是当我跟踪它时,它只是开始播放mp3,并且控制流也在主线程中继续进行。那么它是什么意思阻止了主线程,显然它并没有这样做...

其次,如果我在第一个sp.Play(path2)之后再添加另一个,则执行该命令会停止第一个并开始第二个。这里到底发生了什么?是C#进行内存管理并在我对同一变量执行第二个线程的Join时杀死第一个线程吗?为什么它不能同时播放两个线程?

谢谢

2个回答

3
首先,在大多数情况下,创建一个新线程然后立即在其上执行“Join”没有实际意义。您可以直接在原始线程中执行任何操作;这将完成相同的操作,而无需花费创建、管理和撤销线程的昂贵时间。
至于为什么您的程序尽管执行了“Join”,但仍然继续执行,那是因为您的“PlayMp3”和“PlayWav”方法是异步的。它们开始播放音乐,但这些方法不会阻塞,直到歌曲播放完毕;它们几乎立即返回。因为它们返回,所以线程很快就完成了,因此您的“Join”方法也很快就返回了。如果您的线程正在执行需要相当长时间的工作,则会看到“Join”等待它完成。
至于为什么后续对“Play”的调用会停止之前的歌曲,这与此处显示的代码完全无关。似乎您的“PlayMp3”和“PlayWav”方法被设计成,如果在另一首歌曲仍在播放时调用它们,它们将停止那些歌曲。这与您展示的代码无关。

是的,很有道理。我正在使用WMPlib.WindowsMediaPlayer,我猜它是异步播放的,而我创建的线程只是临时存在,用于触发媒体播放器,然后终止。此外,第二个播放会停止第一个播放,因为我使用了相同的媒体播放器对象。如果每次都创建一个新的对象,那么它就可以并行播放。谢谢。 - shek

1
当一个线程调用Join()时,它表示愿意停止在该线程上的进一步处理,直到目标线程完成运行。通常我在试图关闭东西以使事情有序发生时使用Join()。我几乎从不从用户界面线程中调用Join(),因为我不希望它停止处理用户输入。 PlayMp3PlayWav做什么?根据您的描述,我的猜测是它们将指定的文件交给媒体播放器,然后退出。因此,您的主线程启动一个工作线程将文件交给具有其自己的线程模型的另一个程序,然后工作线程退出。一旦发生这种情况,主线程继续执行,因为它正在等待的线程(通过Join())已经退出。
尝试这个。在工作线程退出之前,让它睡眠10秒钟。当主线程调用Join()时,您应该看到主线程暂停10秒钟。

现在完全明白了。我的PlayMp3正是你猜测的那样。将工作线程休眠确实会阻塞我的主线程。谢谢。 - shek

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