1

スレッドセーフキューで.wavファイルを再生しようとしています。呼び出しは任意のスレッドによってランダムにトリガーできますが、順番に再生する必要があります。問題は、たとえば3つのファイル名(現在UIスレッドからテスト中)で呼び出すPlay(string[] files)と、最後のファイルが3回再生され、最初の2つは再生されないことです。何が原因なのかわからない…?

これを行うためのより簡単な方法の提案をいただければ幸いですが、主にこれが機能しない理由を知りたいと思います。

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 ); 
    }
}
4

1 に答える 1

3

これは、クロージャー内のループ変数をキャプチャするという古典的な問題です。

ループ変数のコピーを取得する必要があります。つまり

    foreach (string f in files.ToList())
    {
        var copy = f;
        Console.WriteLine("Queue file: " + f);
        actionQueue.Add(() => PlaySound(copy));
    }

actionQueueループが終了するまでデリゲートが実行されないためです。もちろん、その時までに、変数のf値は変更されています。

于 2013-02-21T16:06:19.707 に答える