尝试在线程安全队列中播放.wav文件。任何线程都可以随机触发呼叫,但必须按顺序播放。问题是当我用3个文件名(目前正在从UI线程测试)调用Play(string[] files)
时,最后一个文件被播放3次,而且这两个文件从未播放过。看不出是什么原因造成的?
希望有更简单的方法来做到这一点的建议,但主要是想知道为什么这不起作用。
public class Audio
{
static ActionQueue actionQueue;
public static string MediaFolder { get; set; }
private static object syncroot = new object();
static Audio()
{
actionQueue = new ActionQueue();
}
/// <summary>
/// Plays .wav in async queue.
/// </summary>
/// <param name="fileName"></param>
public static void Play(string fileName)
{
actionQueue.Add(() => PlaySound(fileName));
}
public static void Play(string[] files)
{
Console.WriteLine("ID0: " + Thread.CurrentThread.ManagedThreadId);
foreach (string f in files.ToList())
{
Console.WriteLine("Queue file: " + f);
actionQueue.Add(() => PlaySound(f));
}
}
private static void PlaySound(string f)
{
Console.WriteLine("ID1: " + Thread.CurrentThread.ManagedThreadId);
var fileName = f;
Console.WriteLine("Play queue: " + fileName);
fileName = Path.Combine(MediaFolder, fileName);
if (!Path.HasExtension(fileName))
fileName = fileName + ".wav";
if (!File.Exists(fileName)) return;
string ext = Path.GetExtension(fileName);
if (ext != ".wav") return;
Console.WriteLine("Playing: " + fileName);
SoundPlayer player = new SoundPlayer(fileName);
player.PlaySync();
}
}
public class ActionQueue
{
private BlockingCollection<Action> persisterQueue = new BlockingCollection<Action>();
public ActionQueue( )
{
var thread = new Thread(ProcessWorkQueue);
thread.IsBackground = true;
thread.Start();
}
private void ProcessWorkQueue()
{
while (true)
{
var nextWork = persisterQueue.Take();
nextWork();
}
}
public void Add(Action action)
{
persisterQueue.Add(action );
}
}
答案 0 :(得分:3)
这是经典的captured-loop-variable-in-a-closure问题。
你需要复制你的循环变量,即
foreach (string f in files.ToList())
{
var copy = f;
Console.WriteLine("Queue file: " + f);
actionQueue.Add(() => PlaySound(copy));
}
您的委托传递给actionQueue
的原因在循环结束之前不会被执行。当然,到那时,变量f
已经改变了值。