c++ 如何修复此代码中的“terminate called without an active exception”错误?

b5buobof  于 2023-05-30  发布在  其他
关注(0)|答案(1)|浏览(225)

首先,我是一个学生,所以代码可能不是一个有效的或写得很好的。抱歉但如果你不介意的话我需要你的帮助。
下面是我的主程序和我在主程序中使用的musical_chairs函数。

void musical_chairs(IntQueueHW6 &my_queue, unsigned int idx,unsigned int remaining_player_num, vector<int> &players)
{

    this_thread::sleep_until(chrono::system_clock::now() + chrono::seconds(2));
    mtx.lock();
    if(!(my_queue.isFull()))
    {
        my_queue.enqueue(idx);// my_queue is a queue with size of total_player_num-1
        cout << "Player " << idx << " captured a chair at ";
        current_time();
        cout << "." << endl;
    }
    else
    //**************** I believe this block causing the error *******************
        not_capture(idx);
        for(int k = 0; k < remaining_player_num; k++)
        {
            if(players[k] == idx)
            {
                players.erase(players.begin()+k);
                break;
            }
        }
    }
    mtx.unlock();
//*************** I believe this block causing the error **********************
}

int main()
{
    unsigned int total_player_num, total_round_num, remaining_player_num;
    cout << "Welcome to Musical Chairs game!" << endl << "Enter the number of players in the game: " << endl;
    cin >> total_player_num;
    remaining_player_num = total_player_num;
    total_round_num = total_player_num - 1;
    cout << "Game Start!" << endl << endl;

    vector<int> players(total_player_num); // creating a vector consists of players IDs
    for(int i=0; i < total_player_num; i++)
    {
        players[i] = i;
    }

    while(remaining_player_num != 1) // until only one player left in te game
    {
        IntQueueHW6 my_queue(total_round_num); // creating a queue with size total_round_num for chair slots
        vector<thread> threads(total_player_num); // create a thread vector for all players

        cout << "Time is now ";
        current_time();
        cout << endl;
        int players_length = players.size();

        for(int i = 0; i < players_length; i++)
        {
            int idx = players[i];
            threads[idx] = thread(&musical_chairs, ref(my_queue), idx, remaining_player_num, ref(players));
        }
        
        for(int i = 0; i < players_length; i++) //joining to threads
        {
            int idx = players[i];
            if(threads[idx].joinable())
            {
                threads[idx].join();
            }
        }
         
        display_remaining_players(players); // changing variables accordingly
        remaining_player_num --;
        total_round_num -- ;
        my_queue.clear();
    }

    unsigned int winner_id = players[0];
    winner_func(winner_id); // this function displays the winner

    return 0;
}

我用多线程写了一个模拟“抢椅子”游戏的程序。当我运行主程序时,我得到“没有活动异常就终止调用”错误。由于我在互联网上的研究很少,这可能是因为线程超出范围,但如果是这样,我不知道如何修复它。谁能解释一下怎么解决?如果错误发生在不同的事情,你能解释一下吗?你能帮助我修复这个代码吗?先谢谢你了。

4si2a6ki

4si2a6ki1#

调用std::terminate是因为一个线程没有被联接就终止了。
musical_chairs函数中的失败线程将自己从播放器向量中删除,但与此同时,main线程继续执行,循环调用join的线程。这里缺少同步将导致随机失败,具体取决于线程调度的变化。
考虑到代码结构,这将需要重新考虑才能修复。下面是我能想到的最简单的实现。它使用std::atomic而不是std::mutex,这样效率更高。
几个快速注解:

  • 我为玩家选择了std::set而不是std::vector,因为玩家ID是唯一的,并且从集合中删除元素比向量更容易和更有效。
  • 为了使游戏更公平,我选择middle线程作为每一轮的失败者--否则,最后一个线程会经常失败。
  • 线程创建的lambda捕获是微妙的。pid必须通过值捕获,这样每个线程都有自己的播放器id,但是queuechairsloser需要通过引用捕获,因为它们都是跨线程共享的。

示例代码

#include <chrono>
#include <iostream>
#include <mutex>
#include <queue>
#include <set>
#include <thread>
#include <vector>

using std::cin, std::cout, std::endl;
using namespace std::chrono_literals;

using PlayerId = int;
using Queue = std::queue<PlayerId>;
using Players = std::set<PlayerId>;

int main(int argc, const char *argv[]) {
    int initial_number_players{};
    cin >> initial_number_players;

    Players players;
    for (auto i = 0; i < initial_number_players; ++i)
        players.insert(i);

    while (players.size() > 1) {
        Queue queue;
        std::vector<std::thread> threads;

        std::atomic<int> chairs{}, loser{};
        for (auto pid : players) {
            threads.emplace_back([&,pid]() {
                std::this_thread::sleep_for(250ms);
                auto cid = chairs.fetch_add(1);
                if (cid == players.size() / 2)
                    loser = pid;
            });
        }
        for (auto& th : threads)
            if (th.joinable())
                th.join();

        cout << "Player " << loser << " did not capture a chair" << endl;
        players.erase(loser);
    }

    cout << "Player " << *players.begin() << " won" << endl;

    return 0;
}

输出

Player 8 did not capture a chair
Player 0 did not capture a chair
Player 1 did not capture a chair
Player 2 did not capture a chair
Player 6 did not capture a chair
Player 3 did not capture a chair
Player 5 did not capture a chair
Player 4 did not capture a chair
Player 9 did not capture a chair
Player 7 won

相关问题