在实践现代 C++17 最佳实践的同时管理线程

Fra*_*ler 5 c++ multithreading mutex class-design c++17

最初我曾想过设计一个ThreadManager类来threads与它们一起存储data type objects并且function type objects可以使用。该类负责管理内存、访问、传输、释放、锁定、解锁、加入和标准多线程库中相关类型的其他典型通用功能。它最初的目的是将包含线程及其 id 与特定线程可以访问的一组特定资源相关联。

通过文档阅读后cppreferencemutexshared_mutexlock_guardshared_lockstd::function<...>,等现在知道mutexeslock_guards是非可复制,事实上,如果我模板类存储任意function objectsfunction pointerslambdasstd::function<>S作为std::function<>这个类的容器内的这一类的实例化预期的单例只能存储特定的函数签名,将其限制为无法实例化任何其他声明签名。

关于为标准库中的多线程库的这些行为和属性mutexesshared_mutexes lock_guardsthreadspromisesfutures等...它来到介意,我这个得太多类的整体设计。

您可以通过我之前提出的这个问题来参考我最初的设计尝试。在不知道它们的声明签名的情况下将任意函数对象存储到类成员容器中,这应该让您了解我正在尝试做什么。


进一步了解他们的行为、属性和职责,我想知道以下内容是否适合预期的设计过程。

而不是存储任何mutexes, lock_guards, threads,data type objectsfunction objects; 仅存储 生成idsthreads,并让我的管理器类更像监视、记录和报告类型类是否更有意义?

我的新意图是容器将线程的 ID 存储在关联的映射中作为其键以及关联的公共结构。该结构将包含组合资源的所有职责和操作的属性列表。这可能允许支持以下某些功能:优先级队列、任务调度程序、发送和获取资源的命令调度程序,知道线程是否可用,此类不会执行这些类型的操作直接但通过通用函数模板。

例如:

struct ThreadProperties {
    // thread specific
    id,
    slept for,
    is locked,
    is waiting,
    is joined,
    has mutex, if so hold id to mutex - lockguard
    is shared,
    is active,
    has promise or future...
    who has ownership of,
    marked for release,
    marked for transfer

    // other mutex and lock_guard properties

    // function object address stored as `size_t` to represent an id
    // data object address stored as `size_t` to represent an id

    // etc.

};

class ThreadManager {
private:
    std::map<unsigned, ThreadProperites> threadTable;
public:
    default constructor

    storeIds into relevant containers

    store properties into relevant containers

    associate above containers into a map or lookup table

    find or look for specific ID and if found 
    check to see its current status and report it
    also check to see if it's in a priority queue 
    or task scheduler and determine if it is ready
    to do something, or change its internal state.

    other common methods of functionality associated with threads.
};

// function templates to act on threads according to the reporting of the manager above.
Run Code Online (Sandbox Code Playgroud)

我打算实现这种设计,同时努力保持面向 c++17 的现代 c++ 的最佳实践;这种设计是否适合适当的类设计,使其具有通用性、模块化、可移植性和使用效率?

Yak*_*ont 3

线程和其他std原语就像原始指针。您应该构建一个不会暴露任何低级别内容的并发模型。线程std原语为您提供了足够的工具来执行此操作。

了解一些正在开发中的新东西——执行器、流、协程、rangesv3、monadic future 等。围绕它们对你的库进行建模。

试图基于互斥体的原始使用、线程的睡眠和唤醒、阻塞、原子和共享数据来编写行为良好的代码是一个陷阱。

举个例子:

struct thread_pool;
template<class T>
struct my_future:std::future<T> {
  template<class F>
  auto then( F&& f )&&
  -> std::future< std::result_of_t<F(T&&)> >;
  thread_pool* executor = 0;
};
template<>
struct my_future<void>:std::future<void> {
  template<class F>
  auto then( F&& f )&&
  -> std::future< std::result_of_t<F()> >;
  thread_pool* executor = 0;
};
struct thread_pool {
  template<class F=do_nothing>
  my_future<std::result_of_t<F()>> do_task(F&& f={});
};
Run Code Online (Sandbox Code Playgroud)

在这里,我们讨论从一个任务到另一个任务的管道数据,并以增强的future<T>. future<X>通过拆分(通过shared_future)和合并(连接future<Y>以产生future<X, Y>)的能力来增强它。

也许更进一步,构建一个基于流的系统:

template<class In>
using sink = std::function<void(In)>;
template<class Out>
using source = std::function<sink<Out>>;
template<class In, class Out>
using pipe = std::function< source<In>, sink<Out> >;
Run Code Online (Sandbox Code Playgroud)

然后支持将源转换为异步源。

不要建造一些巨大的抽象城堡并希望它是完整的,而是阅读这些内容,当你遇到问题时,其中一个内容可以解决,实施足以解决你的问题。第一次尝试时,您并不是从头开始编写最终的线程系统,所以不要尝试。写一些有用的东西,然后下次写一个更好的。