久久久久久久av_日韩在线中文_看一级毛片视频_日本精品二区_成人深夜福利视频_武道仙尊动漫在线观看

如何利用 Qt 使 QObject 方法線程安全?

How to leverage Qt to make a QObject method thread-safe?(如何利用 Qt 使 QObject 方法線程安全?)
本文介紹了如何利用 Qt 使 QObject 方法線程安全?的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!

問題描述

假設我們在一個 QObject 派生類中編寫了一個非常量方法:

Suppose we wrote a non-const method in a QObject-deriving class:

class MyClass : public QObject {
  int x;
public:
  void method(int a) {
    x = a; // and possibly other things
  };
};

我們想讓該方法成為線程安全的:這意味著從任意線程調用它,并且從多個線程并發調用,不應引入未定義的行為.

We want to make that method thread-safe: meaning that calling it from an arbitrary thread, and from multiple threads concurrently, shouldn't introduce undefined behavior.

  1. Qt 提供哪些機制/API 來幫助我們使該方法成為線程安全的?

  1. What mechanisms/APIs does Qt provide to help us make that method thread-safe?

Qt 的哪些機制/API 在該方法還可以完成其他事情"時可以使用?

What mechanisms/APIs from Qt one could use when the method does the "other things" too?

是否可以對其他事物"進行分類,以告知要使用哪些 Qt 特定機制/API?

Is there any classification possible of the "other things" that would inform what Qt-specific mechanisms/APIs to use?

題外話是 C++ 標準本身提供的機制,以及確保線程安全的通用/非 Qt 特定方法.

Off topic are mechanisms provided by the C++ standard itself, and generic/non-Qt-specific ways of ensuring thread-safety.

推薦答案

適用的 Qt API 取決于線程安全方法的功能是什么.讓我們從最普遍到最具體的情況進行介紹.

The applicable Qt APIs depend on what is the functionality of the thread-safe method. Let's cover the circumstances from the most general to most specific.

信號體由 moc 工具生成并且是線程安全的.

The bodies of signals are generated by the moc tool and are thread-safe.

推論 1:所有直接連接的槽/函子必須是線程安全的:否則破壞信號契約.雖然信號槽系統允許代碼解耦,但直接連接的特定情況會將信號的要求泄漏給連接的代碼!

Corollary 1: All directly-connected slots/functors must be thread-safe: doing otherwise breaks the contract of a signal. While the signal-slot system allows decoupling of code, the specific case of a direct connection leaks the requirements of a signal to the connected code!

推論 2:直接連接比自動連接更緊密.

最通用的方法是確保方法始終在對象的thread() 中執行.這使得它在對象方面是線程安全的,但當然,在方法內使用任何其他對象也必須是線程安全的.

The most general approach is that of ensuring that the method's is always executed in the object's thread(). This makes it thread-safe in respect to the object, but of course the use of any other objects from within the method must be done thread-safely too.

一般來說,線程不安全的方法只能從對象的thread()中調用:

In general, a thread-unsafe method can only be called from the object's thread():

void MyObject::method() {
  Q_ASSERT(thread() == QThread::currentThread());
  ...
}

無線程對象的特殊情況需要注意.當一個對象的線程結束時,它變成無線程的.然而,僅僅因為對象是無線程的并不能使其所有方法都是線程安全的.出于線程安全的目的,最好選擇一個線程來擁有"此類對象.這樣的線程可能是主線程:

The special case of a thread-less object requires some care. An object becomes thread-less when its thread finishes. Yet, just because the object is thread-less doesn't make all of its methods thread-safe. It would be preferable to choose one thread to "own" such objects for the purpose of thread-safety. Such thread might be the main thread:

Q_ASSERT(QThread::currentThread() == (thread() ? thread() : qApp()->thread()));

我們的工作就是實現這一主張.方法如下:

Our job is to fulfill that assertion. Here's how:

  1. 利用線程安全信號.

  1. Leverage thread-safe signals.

由于信號是線程安全的,我們可以使我們的方法成為信號,并將其實現托管在插槽中:

Since signals are thread-safe, we could make our method a signal, and host its implementation in a slot:

class MyObject : public QObject {
  Q_OBJECT
  int x;
  void method_impl(int a) {
    x = a;
  }
  Q_SIGNAL void method_signal(int);
public:
  void method(int a) { method_signal(a); }
  MyObject(QObject * parent = nullptr) : QObject{parent} {
    connect(this, &MyObject::method, this, &MyObject::method_impl);
  }
};

這種方法可以支持斷言,但很冗長,并且每個參數都執行額外的動態分配(至少從 Qt 5.7 開始).

This approach works to uphold the assertion, but is verbose and performs an additional dynamic allocation per each argument (as of Qt 5.7 at least).

將函子中的調用分派給對象的線程.

Dispatch the call in a functor to the object's thread.

有很多方法;讓我們展示一個執行最少動態分配的方法:在大多數情況下,只有一個.

There are many ways of doing it; let's present one that does the minimum number of dynamic allocations: in most cases, exactly one.

我們可以將方法的調用包裝在一個函子中,并確保它以線程安全的方式執行:

We can wrap the call of the method in a functor and ensure that it's executed thread-safely:

void method1(int val) {
   if (!isSafe(this))
      return postCall(this, [=]{ method1(val); });
   qDebug() << __FUNCTION__;
   num = val;
}

如果當前線程是對象的線程,則沒有開銷,也沒有數據復制.否則,調用將推遲到對象線程中的事件循環,如果對象是無線程的,則調用將推遲到主事件循環.

There is no overhead and no copying of data if the current thread is the object's thread. Otherwise, the call will be deferred to the event loop in the object's thread, or to the main event loop if the object is threadless.

bool isSafe(QObject * obj) {
   Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread());
   auto thread = obj->thread() ? obj->thread() : qApp->thread();
   return thread == QThread::currentThread();
}

template <typename Fun> void postCall(QObject * obj, Fun && fun) {
   qDebug() << __FUNCTION__;
   struct Event : public QEvent {
      using F = typename std::decay<Fun>::type;
      F fun;
      Event(F && fun) : QEvent(QEvent::None), fun(std::move(fun)) {}
      Event(const F & fun) : QEvent(QEvent::None), fun(fun) {}
      ~Event() { fun(); }
   };
   QCoreApplication::postEvent(
            obj->thread() ? obj : qApp, new Event(std::forward<Fun>(fun)));
}

  • 將調用分派到對象的線程.

  • Dispatch the call to the object's thread.

    這是上述的變體,但沒有使用函子.postCall 函數可以顯式包裝參數:

    This is a variation on the above, but without using a functor. The postCall function can wrap the parameters explicitly:

    void method2(const QString &val) {
       if (!isSafe(this))
          return postCall(this, &Class::method2, val);
       qDebug() << __FUNCTION__;
       str = val;
    }
    

    那么:

    template <typename Class, typename... Args>
    struct CallEvent : public QEvent {
       // See https://stackoverflow.com/a/7858971/1329652
       // See also https://stackoverflow.com/a/15338881/1329652
       template <int ...> struct seq {};
       template <int N, int... S> struct gens { using type = typename gens<N-1, N-1, S...>::type; };
       template <int ...S>        struct gens<0, S...> { using type = seq<S...>; };
       template <int ...S>        void callFunc(seq<S...>) { (obj->*method)(std::get<S>(args)...); }
       Class * obj;
       void (Class::*method)(Args...);
       std::tuple<typename std::decay<Args>::type...> args;
       CallEvent(Class * obj, void (Class::*method)(Args...), Args&&... args) :
          QEvent(QEvent::None), obj(obj), method(method), args(std::move<Args>(args)...) {}
       ~CallEvent() { callFunc(typename gens<sizeof...(Args)>::type()); }
    };
    
    template <typename Class, typename... Args> void postCall(Class * obj, void (Class::*method)(Args...), Args&& ...args) {
       qDebug() << __FUNCTION__;
       QCoreApplication::postEvent(
                obj->thread() ? static_cast<QObject*>(obj) : qApp, new CallEvent<Class, Args...>{obj, method, std::forward<Args>(args)...});
    }
    

  • 保護對象的數據

    如果該方法對一組成員進行操作,則可以使用互斥鎖來序列化對這些成員的訪問.利用 QMutexLocker 表達您的意圖,并通過構造避免未發布的互斥鎖錯誤.

    Protecting the Object's Data

    If the method operates on a set of members, the access to these members can be serialized by using a mutex. Leverage QMutexLocker to express your intent and avoid unreleased mutex errors by construction.

    class MyClass : public QObject {
      Q_OBJECT
      QMutex m_mutex;
      int m_a;
      int m_b;
    public:
      void method(int a, int b) {
        QMutexLocker lock{&m_mutex};
        m_a = a;
        m_b = b;
      };
    };
    

    在使用特定于對象的互斥鎖和在對象線程中調用方法體之間的選擇取決于應用程序的需要.如果方法中訪問的所有成員都是私有的,那么使用互斥鎖是有意義的,因為我們處于控制之中,并且可以通過設計確保所有訪問都受到保護.使用特定于對象的互斥鎖也將方法與對象事件循環上的爭用分離開來——因此可能具有性能優勢.另一方面,如果方法必須訪問它不擁有的對象上的線程不安全的方法,那么互斥鎖就不夠用了,方法的主體應該在對象的線程中執行.

    The choice between using an object-specific mutex and invoking the body of the method in the object's thread depends on the needs of the application. If all of the members accessed in the method are private then using a mutex makes sense since we're in control and can ensure, by design, that all access is protected. The use of object-specific mutex also decouples the method from the contention on the object's event loop - so might have performance benefits. On the other hand, is the method must access thread-unsafe methods on objects it doesn't own, then a mutex would be insufficient, and the method's body should be executed in the object's thread.

    如果 const 方法讀取可以包裝在 QAtomicIntegerQAtomicPointer 中的單個數據,我們可以使用原子字段:

    If the const method reads a single piece of data that can be wrapped in a QAtomicInteger or QAtomicPointer, we can use an atomic field:

    class MyClass : public QObject {
      QAtomicInteger<int> x;
    public:
      /// Thread-Safe
      int method() const {
        return x.load();
      };
    };
    

    修改簡單的成員變量

    如果該方法修改了可以包裝在QAtomicIntegerQAtomicPointer 中的單個數據,則可以使用原子原語,我們可以使用原子字段:

    Modifying a Simple Member Variable

    If the method modifies a single piece of data that can be wrapped in QAtomicInteger or QAtomicPointer, and the operation can be done using an atomic primitive, we can use an atomic field:

    class MyClass : public QObject {
      QAtomicInteger<int> x;
    public:
      /// Thread-Safe
      void method(int a) {
        x.fetchAndStoreOrdered(a);
      };
    };
    

    這種方法一般不會擴展到修改多個成員:某些成員更改而其他成員不更改的中間狀態將對其他線程可見.通常這會破壞其他代碼所依賴的不變量.

    This approach doesn't extend to modifying multiple members in general: the intermediate states where some members are changed and some other are not will be visible to other threads. Usually this would break invariants that other code depends on.

    這篇關于如何利用 Qt 使 QObject 方法線程安全?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!

    【網站聲明】本站部分內容來源于互聯網,旨在幫助大家更快的解決問題,如果有圖片或者內容侵犯了您的權益,請聯系我們刪除處理,感謝您的支持!

    相關文檔推薦

    How can I read and manipulate CSV file data in C++?(如何在 C++ 中讀取和操作 CSV 文件數據?)
    In C++ why can#39;t I write a for() loop like this: for( int i = 1, double i2 = 0; (在 C++ 中,為什么我不能像這樣編寫 for() 循環: for( int i = 1, double i2 = 0;)
    How does OpenMP handle nested loops?(OpenMP 如何處理嵌套循環?)
    Reusing thread in loop c++(在循環 C++ 中重用線程)
    Precise thread sleep needed. Max 1ms error(需要精確的線程睡眠.最大 1ms 誤差)
    Is there ever a need for a quot;do {...} while ( )quot; loop?(是否需要“do {...} while ()?環形?)
    主站蜘蛛池模板: 亚洲少妇综合网 | 一区不卡在线观看 | 蜜桃视频在线观看免费视频网站www | 国产精品久久久久久久久久免费看 | 91视频导航 | 亚洲福利在线观看 | 亚洲国产精品久久久久 | 羞视频在线观看 | xxxxx免费视频 | 欧美日韩视频一区二区 | 欧美一区二区在线播放 | 国产精品久久久久久久久久 | 国产一级免费在线观看 | 精品视频一区二区三区 | 欧美在线观看一区 | 麻豆av电影网 | 中文字幕在线观看一区二区 | 国产特一级黄色片 | 99久久久国产精品免费消防器 | 欧美一级特黄aaa大片在线观看 | 亚洲一区二区久久 | 国产成人一区二区三区久久久 | 国产成人短视频在线观看 | 成人在线视频一区 | 日韩精品在线观看一区二区 | 久久日韩精品一区二区三区 | 久久av一区二区三区 | 亚洲精品v日韩精品 | 97精品超碰一区二区三区 | 日韩欧美在线视频 | 欧美日本韩国一区二区三区 | 一区二区不卡视频 | 久久www免费人成看片高清 | 欧美电影免费观看高清 | 日韩在线不卡视频 | 精品国产不卡一区二区三区 | 日韩视频―中文字幕 | 精品久久久久久 | 免费毛片网站 | 国产精品夜夜春夜夜爽久久电影 | 一区二区三区国产精品 |