2017年2月24日 星期五

01 C++: C++ Concurrency Notes

01 C++: C++ Concurrency Notes

multiprocessing files pipes mq
multithread shared memory
threads pros fast to share memory low overhead cons: difficult to implement, does not scale to distributed system

What must do after std::thread t1(function1)?
t1.join() or t1.detach() making daemon thread

How many times a thread can be join or detach?
A thread should be join() or detach() once, can checked by joinable()

What if a thread not joined or detached?
Program terminates when a thread object t1 is destroyed before join or detach, so must join or detach before thread object out of scope

What argument needs for a thread?
A function or any callable object such as function object or lambda.

How to create a function object?
void Functor::operater()() {}
Functor ft;
Then std::thread t1(ft);
But std::thread t1(Functor()); does not work, does not compile. It is not creating a thread object but it is a function declaration.
Need std::thread t1((Functor()));

How to pass a string to the functor via thread?
Pass the string to second argument for thread, i.e. std::thread t1((Functor()), s)

Is the string to thread passed by value or by reference?
Even void Functor::operator()(string& s){} allowing reference argument, the string is passed by value because argument is always passed by value to thread.

How to pass argument to thread by reference?
std::thread t1((Functor()), std::ref(s));

How to avoid string memory sharing between main thread and child thread?
std::thread t1((Functor()), std::move(s));

How to get thread id?
std::this_thread()::get_id() for main thread
t1.get_id() for child thread

How many thread is suitable?
Number of cores, otherwise bad over subscription causing context switch to degrade performance. Use std::thread::hardware_concurrency() get get number of threads that can be truly run on hardware. 

What is data race?
Threads racing common resource an outcome depends on execution order of threads

How to solve data race?
Use mutex to protect the shared resource to lock and unlock.

How to take care of exception after mutex lock?
Use std::lock_guard<std::mutex> lockguard(mu);

How to bundle the mutex with shared resource?
Create a class to encapsulate mutex and shared ofstream. Remember never return the shared ofstream by reference to the user code such as ofstream& getStream() {return ofs;} and never accept a user provided function that operates on the shared ofstream such as void processf(void fun( ofstream&)) { fun(f);}

Would it be sufficient to protect the shared resource by adding a mutex to each public function?
No. when more than one public function interface needs to be used together to provide an atomic operation, the protection fails. Such as stack.top()and stack.pop(). Need to combine the inherently non thread safe interfaces into one. 

What are deadlock conditions?
Mutual exclusion
Hold and wait (right hold left wait)
No preemption (left cannot preempt)
Circular waiting (waiting left)

How to avoid deadlock?
Maintain the lock order to lock m1 then m2
Use std::lock(m1, m2) to lock and use std::lock_guard(m1, std::adopt_lock) for adopting the ownership of mutex without locking it initially but unlock it when out of scope.

What is lock granularity?
A lock making large scope of resources has course granular, losing concurrency. A fine grained lock protect small amount of resourceeasily causing deadlock. 

How many ways to lock mutex?
  1. m1.lock() and m1.unlock() manually
  2. std::lock_guard(m1)
  3. std::lock(m1, m2)
  4. std::unique_lock(m1, std::defer_lock)

How to provide more fine grained of using locking using lock_guard?
std::unique_lock ulock(m1, std::defer_lock)
Then manual ulock.lock() and ulock.unlock() multiple times. 

Once Flag

What std::once_flag?
std::call_once(std::once_flag, lambda)

Condition Variable
it solves the problem of producer consumer busying waiting problem that producer notifies one thread that waits on the cv that listen on the lock holding on that mutex to wake up, allowing threads to execute on predefined order. The consumer threads use a condition variable to wait on the locked lock holding a mutex, unlock the mutex to allow producer to put data, and then immediately goes to sleep. When consumer thread is waked up, it immediately locks the lock that holds the mutex and then consumes the data. 

Will a thread using condition variable to wait for a lock of mutex be waken up spontaneously?
Yes. Even though there is no notify_one() be called in the application from any thread, the waiting thread may wake up, which is called spurious wakeup.

How to solve spurious wakeup?
Provide a wait predicate for example a lambda in cv.wait(lock, [](){!q.empty();})
If predicate is true, then truly wake up, else go back to sleep again.

Future and Promise

How to make a thread to call a function with calculated result?
  1. Complicated way: pass a reference result variable using std::ref(x) to the thread second argument so that the function returns the calculated value by reference. Note the referenced result is a shared variable and requires a mutex and condition variable to protect data race and ensure execution order. Note that the main thread waits the results using cv.wait(lock with mutex)
  2. Using std::async(func) that it may or may not create a thread to call func, then the returned std::future object can be used to int x = f.get() where get() can be called only once, otherwise program crashed when get() is called twice or more.
  3. For async() if given std::launch::deferred, the func call defers to get() in main thread, while std::launch::async for a new thread, or logical or deferred and async to let implementation decide which is default
  4. Make a future object, pass the future.get_future() future object by reference to thread to get() that waits for the value being set by main thread to call future.set_value()

What if a promise value is not set?
Promise.get() throws exception std::future_errc::broken_promise

What should be done if a promise has to be broken?
Main thread should call promise.set_exception(std::make_exception_pointer(std::runtime_error("sorry")))

How to broadcast a value to multiple threads?
Create a promise, create a future by assigning to promise.get_future(), create a shared_future<int> sf = f.share() and pass the shared_future to async function argument by value.

How many ways to create thread in c++?
std::thread t1(func, 4)
std::future f1(std::launch::async, func, 4)
std::bind(func, 4)
std::call_once(std::once_flag, func, 4)

How many ways to pass callable object ot thread object?
  1. Pass by value of o
  2. Pass by reference of std::ref(o)
  3. Pass by temporary object O()
  4. Pass a lambda [](){}
  5. Pass a ordinary function f
  6. Pass a member function by value (O::m, o)
  7. Pass a member function by reference (O::m&o)
  8. Pass by moving o std::move(o)
  9. Above applicable to thread, bind, asynccall_once

What is a packaged task?
std::packaged_task<int(int)> t(f); t(4);
int x = t.get_future().get();

How to provide argument to packaged task?
std::packaged_task t<int()> t(std::bind(f, 4)),  t(),int x = t.get_future().get()

How many ways to get a future?
  1. Promise get_future()
  2. Packaged task get_future()
  3. Async() returns future

沒有留言:

張貼留言

2023 Promox on Morefine N6000 16GB 512GB

2023 Promox on Morefine N6000 16GB 512GB Software Etcher 100MB (not but can be rufus-4.3.exe 1.4MB) Proxmox VE 7.4 ISO Installer (1st ISO re...