简体   繁体   English

使用软件事务存储的C ++ std :: vector访问

[英]C++ std::vector access using software transactional memory

I'm currently trying to wrap my head around the problem of thread-safety using C++ STL containers. 我目前正在尝试使用C ++ STL容器解决线程安全问题。 I recently tried to implement a thread safe std::vector by using a std::mutex as a member variable, just to then realize that although I could make member functions thread-safe by locking the lock, I couldn't make lib functions like std::sort thread-safe, since they only get the begin()/end() iterators, which is a result of the fundamental split between containers and algorithms in the STL in general. 我最近尝试通过使用std :: mutex作为成员变量来实现线程安全的std :: vector,只是为了意识到尽管我可以通过锁定锁来使成员函数成为线程安全的,但我无法使lib函数成为可能像std :: sort线程安全的一样,因为它们仅获得begin()/ end()迭代器,这通常是STL中容器和算法之间基本分裂的结果。

So then I thought, if I can't use locks, how about software transactional memory (STM)? 所以我想,如果我不能使用锁,那么软件事务性存储器(STM)呢?

So now I'm stuck with this: 所以现在我被困在这里:

#include <atomic>
#include <cstdlib>
#include <iostream>
#include <thread>
#include <vector>

#define LIMIT 10

std::atomic<bool> start{false};
std::vector<char> vec;

void thread(char c)
{
    while (!start)
        std::this_thread::yield();

    for (int i = 0; i < LIMIT; ++i) {
        __transaction_atomic {
        vec.push_back(c);
        }
    }
}

int main()
{
    std::thread t1(thread, '*');
    std::thread t2(thread, '@');

    start.store(true);

    t1.join();
    t2.join();

    for (auto i = vec.begin(); i != vec.end(); ++i)
        std::cout << *i;

    std::cout << std::endl;

    return EXIT_SUCCESS;
}

Which I compile with: 我编译使用:

g++ -std=c++11 -fgnu-tm -Wall

using g++ 4.8.2 and which gives me the following error: 使用g ++ 4.8.2,这给了我以下错误:

error: unsafe function call to push_back within atomic transaction

Which I kinda get, since push_back or sort or whatever isn't declared transaction_safe but which leaves me with the following questions: 由于push_back或sort或其他未声明为transaction_safe的东西,我可以从中得到以下信息:

a) How can I fix that error? a)如何解决该错误?

b) If I can't fix that error, then what are these transactional blocks usually used for? b)如果我无法解决该错误,那么这些事务性块通常用于什么?

c) How would one implement a lock-free thread-safe vector?! c)如何实现无锁线程安全向量?

Thanks in advance! 提前致谢!

Edit: Thanks for the answers so far but they don't really scratch my itch. 编辑:到目前为止,感谢您的答案,但它们并没有真正解决我的问题。 Let me give you an example: Imagine I have a global vector and access to this vector shall be shared amongst multiple threads. 让我举一个例子:想象一下,我有一个全局向量,并且该向量的访问将在多个线程之间共享。 All threads try to do sorted inserts, so they generate a random number and try to insert this number into the vector in a sorted manner, so the vector stays sorted all the time (including duplicates ofc). 所有线程都尝试进行排序插入,因此它们生成一个随机数,并尝试以排序方式将此数字插入向量中,因此向量始终保持排序状态(包括重复的c)。 To do the sorted insert they use std::lower_bound to find the "index" where to insert and then do the insert using vector.insert(). 为了执行排序的插入,他们使用std :: lower_bound查找要插入的“索引”,然后使用vector.insert()进行插入。

If I write a wrapper for the std::vector that contains a std::mutex as a member, than I can write wrapper functions, eg insert which locks the mutex using std::lock_guard and then does the actual std::vector.insert() call. 如果我为包含std :: mutex作为成员的std :: vector编写包装器,那么我可以编写包装器函数,例如,insert使用std :: lock_guard锁定互斥锁,然后执行实际的std :: vector。 insert()调用。 But std::lower_bound doesn't give a damn about the member mutex. 但是std :: lower_bound并没有对成员互斥量产生任何伤害。 Which is a feature, not a bug afaik. 这是功能,而不是bug afaik。

This leaves my threads in quite a pickle because other threads can change the vector while someone's doing his lower_bound thing. 这使我的线程陷入了困境,因为其他线程可以在某人执行“ lower_bound”操作时更改向量。

The only fix I can think of: forgett the wrapper and have a global mutex for the vector instead. 我能想到的唯一解决方案:忘记包装程序,而是为向量使用全局互斥锁。 Whenever anybody wants to do anything on/with/to this vector, he needs that lock. 每当有人要对该向量执行任何操作时,都需要该锁。

THATS the problem. 那就是问题所在。 What alternatives are there for using this global mutex. 使用此全局互斥锁有哪些替代方法。 and THATS where software transactional memory came to mind. 以及想到软件事务存储的地方。

So now: how to use STMs on STL containers? 那么现在:如何在STL容器上使用STM? (and a), b), c) from above). (以及a),b),c))。

I believe that the only way you can make an STL container 100% thread safe is to wrap it in your own object (keeping the actual container private) and use appropriate locking (mutexes, whatever) in your object in order to prevent multi-thread access to the STL container. 我相信,使STL容器具有100%线程安全性的唯一方法是将其包装在您自己的对象中(保持实际的容器为私有),并在对象中使用适当的锁定(互斥量,无论如何),以防止出现多线程访问STL容器。

This is the moral equivalent of just locking a mutex in the caller around every container operation. 这在道德上等同于仅在每个容器操作周围在调用方中锁定一个互斥体。

In order to make the container truly thread safe, you'd have to muck about with the container code, which there's no provision for. 为了使容器真正具有线程安全性,您必须仔细研究容器代码,这是没有规定的。

Edit: One more note - be careful about the interface you give to your wrapper object. 编辑:另一个注意事项-请注意提供给包装对象的接口。 You can't very well go handing out references to stored objects, as that would allow the caller to get around the locking of the wrapper. 您不能很好地分发对存储对象的引用,因为这将允许调用者绕开包装程序的锁定。 So you can't just duplicate vector's interface with mutexes and expect things to work. 因此,您不能仅使用互斥量复制vector的接口并期望其工作。

I'm not sure I understand why you cannot use mutexes. 我不确定我为什么不能使用互斥锁。 If you lock the mutex each time you are accessing the vector then no matter what operation you are doing you are certain that only a single thread at a time is using it. 如果每次访问向量时都锁定互斥锁,则无论您执行什么操作,都可以确定一次仅使用一个线程。 There is certainly space for improvement depending on your needs for the safe vector, but mutexes should be perfectly viable. 当然,根据您对安全向量的需求,仍有改进的空间,但是互斥锁应该是完全可行的。

lock mutex -> call std::sort or whatever you need -> unlock mutex 锁定互斥锁->调用std :: sort或您需要的任何内容->解锁互斥锁

If on the other side what you want is to use std::sort on your class, then again it is a matter of providing thread-safe access and reading methods through the iterators of your container, as those are the ones that std::sort needs to use anyway in order to sort a vector, since it is not a friend of containers or anything of the sort. 如果在另一方面,您想要在类上使用std :: sort,那么又是通过容器的迭代器提供线程安全的访问和读取方法的问题,因为那些是std ::的方法sort不是必须使用容器来对向量进行排序,因为它不是容器之类的东西。

You can use simple mutexes to make your class thread safe. 您可以使用简单的互斥锁来使您的类线程安全。 As stated in another answer, you need to use a mutex to lock the vector before use and then unlock after use. 如另一个答案所述,您需要在使用前使用互斥锁锁定向量,然后在使用后解锁。

CAUTION! 警告! All of the STL functions can throw exceptions. 所有STL函数都可以引发异常。 If you use simple mutexes, you will have a problem if any function throws because the mutex will not be released. 如果使用简单的互斥锁,则由于不释放该互斥锁而引发任何函数时,将会出现问题。 To avoid this problem, wrap the mutex in a class that releases it in the destructor. 为避免此问题,请将互斥体包装在一个在析构函数中将其释放的类中。 This is a good programming practice to learn about: http://c2.com/cgi/wiki?ResourceAcquisitionIsInitialization 这是学习以下内容的良好编程习惯: http : //c2.com/cgi/wiki?ResourceAcquisitionIsInitialization

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

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