0

我有后台工作人员的问题。我不知道如何准确地描述它。实际上它是一个游戏,并且与后台工作人员一起 x 毫秒我更新进度条并检查是否有人输/赢或时间到了。如果有人获胜,则游戏结束。如果两名玩家都输了/时间到了,则游戏进入下一轮。当两个玩家都输了时,就会出现问题。SetTime 方法中的 NextRound 方法运行两次。这是代码:

void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    this.SetTime(e.ProgressPercentage);
}

void bw_DoWork(object sender, DoWorkEventArgs e)
{
    Thread.Sleep(3000);
    BackgroundWorker worker = sender as BackgroundWorker;
    int tick = ProgLib.maxTime * 10;

    for (int i = 1; i <= 100; i++)
    {
        if ((worker.CancellationPending == true))
        {
            e.Cancel = true;
            break;
        }
        else
        {
            // Perform a time consuming operation and report progress.
            Thread.Sleep(tick);
            worker.ReportProgress(i);
        }
    }
}

private void SetTime(double k)
{
    this.time.Bar1.Value = k;
    this.time.Bar2.Value = k;

    if (k >= 100 || (Gallery1.hasLost() && Gallery2.hasLost()) || ((Gallery1.isWinner() || Gallery2.isWinner())))
    {
        if (bw == null)
            return;
        bw.CancelAsync();
        bw.Dispose();
        bw = null;

        saveData();
        ProgLib.isAnyoneWinner(Gallery1.isWinner(), Gallery2.isWinner());
        if (ProgLib.gameHasended())
        {
            gameHasEnded();

        }
        else
        {
            next_round();
        }
    }
}

private void next_round()
{
    Thread nextRoundThread = new Thread((Object Send) =>
    {
        MainThread.Send((Object send) =>
        {
            Gallery1.hidePanel.Visibility = Visibility.Visible;
            Gallery2.hidePanel.Visibility = Visibility.Visible;

            ProgLib.nextLetter();//goes to next letter
            LetterToPlay1.setLetter(ProgLib.getArrabicLetter(ProgLib.getCurentLetter()));//sets the next letter
            LetterToPlay2.setLetter(ProgLib.getArrabicLetter(ProgLib.getCurentLetter()));

        }, null);

        Thread SoundThread = new Thread((Object send) =>
        {
            //Here Must Delay enought so the animation stops the hear the bell and the the letter, and then the game starts
            Thread.Sleep(1800);
            ProgLib.playOtherSound(ProgLib.Sounds.Chimes);//Bell Sound
            Thread.Sleep(100);
            //ProgLib.PlayLetterSound(ProgLib.getCurentLetter());//Letter Sound
            ProgLib.playOtherSound(ProgLib.Sounds.Cat_Yawn);//TestOnly
        });
        SoundThread.IsBackground = true;
        SoundThread.Start();

        Thread.Sleep(3000);
        MainThread.Send((Object send) =>
        {
            Gallery1.refresh();//galleries refresh so that the magician hides.
            Gallery2.refresh();//
            Gallery1.hidePanel.Visibility = Visibility.Hidden;//hide the Big Magician of  mistakes
            Gallery2.hidePanel.Visibility = Visibility.Hidden;
        }, null);

    });
    nextRoundThread.IsBackground = true;
    nextRoundThread.Start();

    bw = new BackgroundWorker();
    bw.WorkerSupportsCancellation = true;
    bw.WorkerReportsProgress = true;
    bw.DoWork += new DoWorkEventHandler(bw_DoWork);
    bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
    bw.RunWorkerAsync();
}
4

1 回答 1

3

There's a threading race in your code. Your worker will call ReportProgress() and immediately iterate the loop. Racing past the CancellationPending property check and falling asleep again.

Your SetTime() method runs later. And calls CancelAsync() but that doesn't have any effect at all since the worker is sleeping. Not until it wakes up again, calls ReportProgress() again, iterates the loop and then sees CancellationPending set to true.

Your SetTime() method will be called again, even though you've already ended the game.

Threading is rife with problems like this. A band-aid is to check for CancellationPending after the Sleep() call. Which works 99.999% of the time. Getting to 100% requires a pretty drastic rewrite that uses proper locking.

于 2013-09-15T17:20:16.380 回答