2012年12月2日 星期日

在新的 thread 裡呼叫 method

若需要執行一個可能會花些時間的函式 (e.g., 有 blocking read) 但又不希望停住目前的 thread, 解法之一是產生一個新 thread, 然後在該 thread 執行原本欲執行的函式。先來看看 Python 遇到這種情況要怎麼作:

import thread

class A(object):
    def out(self):
        print 'A.out()'

a = A()
thread.start_new_thread(A.out, (a,))  # 就這行

相當地直覺。

在 C++ 裡想要執行 non-static member function 的話, 就有點惱人。解決的方法很多, 以下是我土法煉鋼的陽春解法。看 code 即可明白, 關鍵是使用 template 以及 pointer-to-member function。算是一個練習 template 的好題目, 還有了解 C++ 編譯時期多型的威力。

t.h

#include <pthread.h>

template<class T>
struct MethodWrapper {
  typedef void(T::*Method)();
  MethodWrapper(T *object, Method method)
      : m_object(object), m_method(method) {}

  void Run() { (m_object->*m_method)(); }

  T *m_object;
  Method m_method;
};

// The interface for pthread's usage.
template <class T>
void* CallMethod(void *v) {
  MethodWrapper<T> *p = (MethodWrapper<T>*)v;
  p->Run();
  delete p;
  return NULL;
}

template <class T>
inline pthread_t RunInNewThread(MethodWrapper<T>* param) {
  pthread_t th;
  pthread_create(&th, NULL, CallMethod<T>, (void*)param);
  return th;
}

f.h

#include <iostream>
#include <sys/syscall.h>

struct A {
  void Out() {
    std::cout << "tid=" << syscall(__NR_gettid)
        << " A.Out()" << std::endl;
  }
};

f.cpp

#include "f.h"
#include "t.h"

/* Compilation: g++ f.cpp -o f -g -Wall -lpthread */
int main(void) {
  A a;
  a.Out();  // Direct call.

  MethodWrapper<A>* m = new MethodWrapper<A>(&a, &A::Out);
  m->Run();  // Called via the wrapper.

  CallMethod<A>(m);  // Called via a template function.

  m = new MethodWrapper<A>(&a, &A::Out);
  pthread_t th = RunInNewThread(m);  // Called in a new thread.
  pthread_join(th, NULL);
  return 0;
}

C++ passing method pointer as template argument 提到用 boost::function0 的解法, 以後再來讀吧。

2012-12-02 更新

Kencommand 補充了簡單的作法:

Btw, 這篇文章主要目的是練習 template, 另外在這裡補上若要土法煉鋼地支援參數時該怎麼辦。

沒有留言:

張貼留言

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...