C++11线程修改std::list

C++11 thread to modify std::list

我会 post 我的代码,然后告诉你我认为它在做什么。

#include <thread>
#include <mutex>
#include <list>
#include <iostream>

using namespace std;

...

//List of threads and ints
list<thread> threads;
list<int> intList;

//Whether or not a thread is running
bool running(false);

//Counters
int busy(0), counter(0);

//Add 10000 elements to the list
for (int i = 0; i < 10000; ++i){
    //push back an int
    intList.push_back(i);
    counter++;
    //If the thread is running, make a note of it and continue
    if (running){
        busy++;
        continue;
    }
    //If we haven't yet added 10 elements before a reset, continue
    if (counter < 10)
        continue;
    //If we've added more than 10 ints, and there's no active thread,
    //reset the counter and launch
    counter = 0;
    threads.push_back(std::thread([&]
        //These iterators are function args
        (list<int>::iterator begin, list<int>::iterator end){
        //mutex for the running bool
        mutex m;
        m.lock();
        running = true;
        m.unlock();

        //Remove either 10 elements or every element till the end
        int removed(0);
        while (removed < 10 && begin != end){
            begin = intList.erase(begin);
            removed++;
        }

        //unlock the running bool
        m.lock();
        running = false;
        m.unlock();
        //Pass into the thread func the current beginning and end of the list
    }, intList.begin(), intList.end()));
}

for (auto& thread : threads){
    thread.join();
}

我认为这段代码所做的是将 10000 个元素添加到列表的末尾。对于我们添加的每 10 个,启动一个(单个)线程删除列表的前 10 个元素(在线程启动时)。

我不希望这会删除每个列表元素,我只是想看看是否可以在从开头删除元素的同时添加到列表的末尾。在 Visual Studio 中,我经常收到“list iterators incompatible”错误,但我认为问题是跨平台的。

我的想法有什么问题吗?我知道这是什么

编辑:

所以我现在看到这段代码是非常不正确的。实际上,我只希望一次激活一个辅助线程来删除元素,这就是为什么我虽然调用 erase 没问题。但是,我不知道如何在不加入线程的情况下声明一个线程,如果我等待它,那么我真的看不到做任何事情的意义。

我应该在循环之前声明我的线程并让它等待来自主线程的信号吗?

澄清一下,我的目标是执行以下操作:我想在一个线程上抓取键盘按下并将它们存储在一个列表中,并且每隔一段时间将它们记录到一个单独线程上的文件中,同时删除这些东西我登录了因为我不想花很多时间写入磁盘,所以我想写入离散块(10 个)。

感谢 Christophe 和其他所有人。现在这是我的代码...我可能使用 lock_guard 不正确。

#include <thread>
#include <mutex>
#include <list>
#include <iostream>
#include <atomic>

using namespace std;

...

atomic<bool> running(false);
list<int> intList;
int busy(0), counter(0);
mutex m;
thread * t(nullptr);

for (int i = 0; i < 100000; ++i){
    //Would a lock_guard here be inappropriate?
    m.lock();
    intList.push_back(i);
    m.unlock();
    counter++;
    if (running){
        busy++;
        continue;
    }
    if (counter < 10)
        continue;
    counter = 0;
    if (t){
        t->join();
        delete t;
    }
    t = new thread([&](){
        running = true;

        int removed(0);
        while (removed < 10){
            lock_guard<mutex> lock(m);
            if (intList.size())
                intList.erase(intList.begin());  
            removed++;
        }
        running = false;
    });
}

if (t){
    t->join();
    delete t;
}

您的代码不适用于以下原因:

  • 你的互斥锁对每个线程都是本地的(每个线程都有自己的副本,只供自己使用:没有线程间同步的机会!)
  • intList 不是原子类型,但您可以从多个线程访问它,从而导致竞争条件和未定义的行为。
  • 您在线程创建时发送给线程的开始和结束可能在执行期间不再有效。

这里有一些改进(查看注释行):

atomic<bool> running(false);    // <=== atomic (to avoid unnecessary use of mutex)
int busy(0), counter(0);
mutex l;   // define the mutex here, so that it will be the same for all threads

for (int i = 0; i < 10000; ++i){
    l.lock();    // <===you need to protect each access to the list
    intList.push_back(i);
    l.unlock();  // <===and unlock 
    counter++;
    if (running){
        busy++;
        continue;
    }
    if (counter < 10)
        continue;
    counter = 0;
    threads.push_back(std::thread([&]
        (){    //<====No iterator args as they might be outdated during executionof threads!!
        running = true;    // <=== no longer surrounded from lock/unlock as it is now atomic

        int removed(0);
        while (removed < 10){
            l.lock();       // <====you really need to protect access to the list
            if (intList.size())    // <=== check if elements exist NOW
                intList.erase(intList.begin());  // <===use current data, not a prehistoric outdated local begin !!
            l.unlock();      // <====end of protected section
            removed++;
        }

        running = false;    // <=== no longer surrounded from lock/unlock as it is now atomic
    })); //<===No other arguments
}
...

顺便说一下,我建议你看看 lock_guard<mutex> 中的锁,因为这些可以确保在所有情况下都能解锁(尤其是当有是例外或像这样的惊喜)。

编辑: 我用互斥锁避免了 running 的锁定保护,方法是将其设置为 atomic<bool>