17 Temmuz 2019 Çarşamba

std::thread Sınıfı

std::thread sınıfı
constructor - default
Thread'in default constructor'ı kullanılırsa nesne yaratılsa bilse thread başlamaz. İleriki bir noktada move assignment operator'ü ile thread başlatılıp, bizim nesnemize atanabilir. Şöyle yaparız.
m_Thread = std::thread(&Foo::loop, this);
constructor - method
std::thread constructor metodu yazısına bakmak önemli.

move constructor
std::thread move edilince içindeki functor std::invoke ile çalıştığı için sorun çıkmıyor. Açıklaması şöyle
A std::thread does not formally own the functor it was constructed with. Rather, the functor is held by the invocation of std::invoke which is kicked off (in the target thread) by the constructor.

So moving a std::thread doesn't need to, and isn't going to, move the functor. As long as the thread is executing, the functor is alive as a local variable.
destructor 
Joinable thread nesnesi thread bitmeden destruct edilirse uygulama sonlanır. Bu yüzden std::shared_ptr<std::thread> pek mantıklı değil.
30.3.1.3 thread destructor [thread.thread.destr] ~thread();

If joinable(), calls std::terminate(). Otherwise, has no effects.

[ Note: Either implicitly detaching or joining a joinable() thread in its destructor could result in difficult to debug correctness (for detach) or performance (for join) bugs encountered only when an exception is raised. Thus the pro grammer must ensure that the destructor is never executed while the thread is still joinable. — end note ]

Ya thread'in bitmesini beklemek ya da aşağıdaki gibi detach olmak gerekir.

detach metodu
Eğer thread scope içinde yaratılıyorsa ve sonucunu beklemek istemiyorsak detach olmamız gerekir.
std::thread thread(ClientLoop,clientSocket);
thread.detach();
// OK to destroy now
Aslında tedbirli olmak için şöyle kodlamak daha iyi.
if (thread.joinable())
  thread.detach ();
detach olurken şuna dikkat etmek gerekir. Eğer thread metodu exception atarsa ve bunu yakalamıyorsa std::terminate çağrılır. Yani şöyle bir kod tüm uygulamayı sonlandırır.
void aFunction() {
   std::thread t {someOtherFunction}; // someOtherFunction may throw!!!
   t.detach;
}
get_id metodu
std::thread::id tipindendir. İşletim sistemi tarafından thread'e atanan tekil numarayı döner. Windows'ta altta GetCurrentThreadId () metodunu çağırıyor. std::this_thread::get_id() ile aynı işlevi yerine getirir.
const std::thread::id MAIN_THREAD_ID = std::this_thread::get_id();
hardware_concurrency metodu
 - İşlemciler genellikle aynı hızlarda oluyorlar. Buna symmetric multiprocessing deniliyor.
 - hyperthreading ise şöyle.
The Intel hyper-threading technology is often disappointing. You probably don't want to have 8 active threads on an Intel processor with 4 cores and 8 hyperthreads.
Örnek
Şöyle yaparız.
//may return 0 when not able to detect
unsigned concurentThreadsSupported = std::thread::hardware_concurrency();
join metodu
Açıklaması şöyle.
With POSIX pthreads, child threads must be joined after they have exited, or else they continue to occupy system resources (like a process table entry in the kernel). This is done via pthread_join(). Windows has a somewhat analogous issue if the process holds a HANDLE to the child thread; although Windows doesn't require a full join, the parent must still call CloseHandle() to release its refcount on the process.

Since std::thread is a cross-platform abstraction, it's constrained by the POSIX requirement which requires the join.
joinable metodu
Thread'in çalışıp çalışmadığını anlamak için kullanılabilir. Çalışan thread - detach edilmediyse - true döner.
class Service
{
public:
    void start();
private:
    std::thread thread;
};
Service::start()
{
  if (thread.joinable())
    return;
}
Scott Meyers'in kitabında şöyle bir yardımcı sınıf var.
class ThreadRAII {
public:
  ThreadRAII(std::thread&& thread): t(std::move(thread)) {}
  ~ThreadRAII() { if (t.joinable()) (t.join(); }

private:
  std::thread t;
};
Bu sınıf r-value ile çalışıyor. reference ile çalışan bir sınıf şöyle.
class ThreadGuard {
public:
  ThreadGuard(std::thread &t_): t(t_) {}
  ~ThreadGuard()
  {
    if(t.joinable()) 
      t.join();
  }
private:
  std::thread &t;
};
Şöyle kullanırız.
void func()
{
    std::thread my_thread(f);
    ThreadGuard thread_guard(my_thread);
}
operator = metodu
Açıklaması şöyle. Bu davranış boos::thread sınıfından farklılık gösteriyor.
"If [the thread object] is joinable, terminate() is called."
Örnek
Şöyle yaparız.
std::thread td1;
td1 = std::thread(func);
Örnek
Elimizde şöyle bir kod olsun.
class ThreadTest 
{
private:
  std::thread mythread;

  int doSomeWork(){
    ...
  }

public:
  void runThread(){
    mythread = std::thread(&ThreadTest::doSomeWork, this);
  }
  ...
};
Eğer şöyle yaparsak yani çalışmakta olan bir thread'i tekrar thread ataması yaparsak
ThreadTest test;
test.runThread();
std::this_thread::sleep_for (std::chrono::seconds(10));
test.runThread();
Çıktı olarak şunu alırız.
terminate called without an active exception
sleep_for metodu
Açıklaması şöyle. Bu metod verilen değerden daha fazla süre uyuyabilir.
Blocks the execution of the current thread for at least the specified sleep_duration.
This function may block for longer than sleep_duration due to scheduling or resource contention delays.
Örnek
Açıklaması şöyle.
Instead of calling sleep_for, yielding the thread's execution slot, you could busy-sleep. That is, loop until a certain amount of time has passed. It'll often get more accurate results at the cost of making that CPU thread unusable for doing anything else.
Şöyle yaparız. Burada calc_overhead() metodunda 100ms geçmesini bulabilmek için harcanan süre hesaplanıyor ve bu süre ikiye bölünüyor.
// get a rough estimate of how much overhead there is in calling buzy_sleep()
std::chrono::nanoseconds calc_overhead() {
    using namespace std::chrono;

    auto init = []() {
        auto end = steady_clock::now() + 100ms;
        while(true) if(steady_clock::now() > end) break;
    };

    time_point<steady_clock> end;
    auto start=steady_clock::now();
    init();
    end = steady_clock::now();

    // The /2 is non-scientific but makes it sleep longer than you want more often than
    // shorter.
    return (end - start - 100ms) / 2;
}

// initialize the overhead constant that will be used in busy_sleep()
static const std::chrono::nanoseconds overhead = calc_overhead();

inline void busy_sleep(std::chrono::nanoseconds t) {
    auto end = std::chrono::steady_clock::now() + t - overhead;
    while(true) if(std::chrono::steady_clock::now() > end) break;
}
swap metodu
Şöyle yaparız.
std::thread td1;
...
std::thread(func).swap(td1);
thread ve exception
Exception thread metodundan kaçmamalıdır. Şöyle yapamayız.
void runComponent()
{
  throw std::runtime_error("oh no!\n");
}

auto control_thread = std::thread(runComponent);
boost sınıfına göre bazı eksiklikler
std::thread ile bazı şeyleri yapmak mümkün değil. Açıklaması şöyle. Bu işler için sınıfın native_handle() metodu kullanılabilir.
- No stack size on thread creation.
- No thread priorities.
Stack boyutunu ayarlamak için boost ile şöyle yapıyoruz.
boost::thread::attributes attrs;
attrs.set_size(4096*10);
boost::thread myThread(attrs, fooFunction, 42);
thread vector
Eğer thread'leri vector içinde kullanmak istersek
std::vector<std::thread> threads;
while (whatever){
    clientSocket = accept(listenSocket, nullptr, nullptr);
    threads.emplace_back(ClientLoop,clientSocket);
}

// later
for (std::thread & t : threads) {
    t.join();
}

// OK to destroy now
threads.clear();

Hiç yorum yok:

Yorum Gönder