c++ - 在 API 和应用程序线程之间共享数据

标签 c++ multithreading c++11 c++17 api-design

我的 API 在自己的线程中计算一些数据:

/*** API is running in its own thread ***/
class API {
public:
    std::shared_ptr<Data> retrieveData() { return mData; }

private:
    std::shared_ptr<Data> mData;
    std::mutex mDataMutex;

    void run () {
        std::thread t([](){
            while (!exitApi) {
                mDataMutex.lock();

                updateData(mData);

                mDataMutex.unlock();
        });
        t.join();
    }
};

使用我的 API 的应用程序将在另一个线程中检索共享数据:

/*** Application is running in another thread ***/
class Application {
private:
    Api mApi;

    void run () {
        std::thread t([](){
            while (!exitApp) {
                std::shared_ptr<Data> data = mApi.retrieveData();

                /* API thread can update the data while the App is using it! */
                useData(data);
        });
        t.join();
    }

如何设计 API,以便应用程序开发人员在检索数据时不会遇到陷阱?我可以想到三个选项,但不喜欢其中任何一个:

  1. API 将返回所有数据的拷贝,而不是共享指针。但是,数据量可能会变得非常大,应避免复制。
  2. API 在将数据移交给应用程序时会锁定数据,并且应用程序需要在执行完所有计算后明确要求 API 再次解锁数据。即使记录正确,也很容易出现死锁。
  3. 当 API 将数据移交给应用程序时,retrieveData 还将返回一个已锁定的 std::unique_lock。一旦应用程序使用完数据,它就必须解锁unique_lock。这可能不太容易出错,但对于应用程序开发人员来说仍然不是很明显。

是否有更好的选择来设计尽可能对开发人员友好的 API(使用现代 C++11 及更高版本)?

最佳答案

TL;DR:将 shared_ptr 与调用解锁的自定义删除器结合使用。

它认为两种主要方法是:

  1. 返回一个不可变的数据结构,以便可以在线程之间共享。这是一个干净的 API,但(如前所述)复制可能会很昂贵。减少复制需求的一些方法是:

    • 使用写时复制数据结构,以便每次只需要复制数据的某些部分。根据您的数据,这可能不是 可能,否则重构工作量太大。
    • 尽可能使用移动引用来降低复制成本。仅此可能还不够,还取决于您的实际数据。
  2. 在可变数据结构周围使用锁。正如所指出的,这需要 API 用户执行可能不明显的额外操作。但智能指针可以用来减轻消费者的负担:

    • 一种更简单的方法是返回一个自定义智能指针,该指针 在其析构函数中解锁:当调用者的作用域关闭时会发生解锁,因此调用者无需担心解锁调用。 API 使用者将通过引用其方法来传递指针。例如func(locking_ptr& ptr)。可以在这里找到一个简单的实现:https://stackoverflow.com/a/15876719/1617480 .
    • 为了能够通过复制而不是通过引用来传递锁定智能指针,需要采用某种引用计数方案。您可能希望在锁定智能指针内部使用 shared_ptr 以避免滚动您自己的线程安全引用计数。更简单的是,将自定义删除器传递给 shared_ptr 来解锁和删除(根本不需要编写智能指针)。
    • 另一种类型的智能指针会围绕锁中的每个取消引用 ->。我认为这不适合这个用例,因为看起来 API 使用者希望获得一致的结果 View 。下面是此类指针的示例: https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Execute-Around_Pointer

关于c++ - 在 API 和应用程序线程之间共享数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48978811/

相关文章:

c++ - 查找两组是否不同的最快方法?

c++ - 消息处理器 Qt

c# - 如何从文本文件中提取文件路径

java - 如何同步我的端口扫描仪,以便结果按照执行顺序显示?

c++ - 为什么 clang 不使用 NRVO 对此进行优化?

以派生类为参数的 C++ 基类构造函数(?)

c++ - 为#include 连接和字符串化宏值

c++ - 将 C++ 静态成员函数声明为其所在类的友元(语法)

java - 将 75 个客户端连接到服务器后连接被拒绝

c++ - 如何等待一组线程完成或 C++ 中的其他事件