简体   繁体   English

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

[英]C++11 thread to modify std::list

I'll post my code, and then tell you what I think it's doing. 我将发布我的代码,然后告诉您我在做什么。

#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();
}

What I think this code is doing is adding 10000 elements to the end of a list. 我认为这段代码正在做的是在列表末尾添加10000个元素。 For every 10 we add, launch a (single) thread that deletes the first 10 elements of the list (at the time the thread was launched). 对于我们添加的每10个,启动一个(单个)线程,该线程将删除列表的前10个元素(在启动线程时)。

I don't expect this to remove every list element, I was just interested in seeing if I could add to the end of a list while removing elements from the beginning. 我不希望这会删除每个列表元素,我只是想看看在从开头删除元素的同时是否可以添加到列表的末尾。 In Visual Studio I get a " list iterators incompatible " error quite often, but I figure the problem is cross platform. 在Visual Studio中,我经常会遇到“ list iterators incompatible ”错误,但我认为问题出在跨平台上。

What's wrong with my thinking? 我的想法怎么了? I know it's something 我知道是这样

EDIT: 编辑:

So I see now that this code is very incorrect. 因此,我现在看到此代码非常不正确。 Really I just want one auxiliary thread active at a time to delete elements, which is why I though calling erase was ok. 真的,我只希望一次激活一个辅助线程来删除元素,这就是为什么我尽管调用“擦除”是可以的。 However I don't know how to declare a thread without joining it up, and if I wait for that then I don't really see the point of doing any of this. 但是,我不知道如何在不加入线程的情况下声明线程,如果我等待它,那么我真的看不到这样做的意义。

Should I declare my thread before the loop and have it wait for a signal from the main thread? 我应该在循环之前声明我的线程,让它等待主线程发出的信号吗?

To clarify, my goal here is to do the following: I want to grab keyboard presses on one thread and store them in a list, and every so often log them to a file on a separate thread while removing the things I've logged. 为了明确起见,我的目标是执行以下操作:我想抓住一个线程上的键盘按键并将它们存储在列表中,并且每隔一段时间将它们记录到另一个线程上的文件中,同时删除我记录的内容。 Since I don't want to spend a lot of time writing to the disk, I'd like to write in discrete chunks (of 10). 由于我不想花很多时间写磁盘,因此我想写离散块(共10个)。

Thanks to Christophe, and everyone else. 感谢克里斯托夫和其他所有人。 Here's my code now... I may be using lock_guard incorrectly. 现在是我的代码...我可能没有正确使用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;
}

Your code won't work for because: 您的代码不适用于:

  • your mutex is local to each thread (each thread has it's own copy used only by itself: no chance of interthread synchronisation!) 您的互斥锁对于每个线程都是本地的(每个线程都有它自己的副本,仅由它自己使用:没有线程间同步的机会!)
  • intList is not an atomic type, but you access to it from several threads causing race conditions and undefined behaviour. intList不是原子类型,但是您可以从多个线程中访问它,从而导致竞争条件和未定义的行为。
  • the begin and end that you send to your threads at their creation, might no longer be valid during the execution. 在创建线程时发送给线程的开始和结束可能在执行期间不再有效。

Here some improvements (look at the commented lines): 这里有一些改进(请看注释行):

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
}
...

By the way, I'd suggest that you have a look at lock_guard<mutex> for the locks, as these ensure the unlock in all circumstances (especially when there are exceptions or orhter surprises like this). 顺便说一句,我建议您看一下lock_guard<mutex>中的锁,因为这些锁可以确保在所有情况下都可以解锁(尤其是在出现此类异常或意外惊喜时)。

Edit: I've avoided the lock protection of running with a mutex, by making it atomic<bool> . 编辑:通过使它成为atomic<bool> ,我避免了使用互斥锁running的锁定保护。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM