Multi Thread
Multi Thread
class App{
private:
int count = 0;
mutex mtx;
public:
void operator()(){
cout <<"hello"<<endl;
for(int i = 0; i < 1000; i++){
lock_guard<mutex> guard(mtx); //call mutex lock
// unique_lock<mutex> guard(mtx);
count ++;
} //will unlock when guard out of scope
}
int getCount(){
return count;
}
};
int main()
{
App app; //class App cần overload operator() để có thể pass argument to thread
thread t1(ref(app));
//cần dùng ref() do biến mutex của class App không được phép copy constructor
//khi pass argument là app (pass value) thì sẽ copy constructor ->error -> cần pass reference
ref(app)
thread t2(ref(app)); //ref() dùng để pass reference
t1.join();
t2.join();
cout << app.getCount();
return 0;
}
6) Returning Values from Threads
6.1 Calculator pi
EX: Tính pi : 1 – 1/3 + 1/5 – 1/7 + 1/9 -…..
double calculate_pi(int terms){
double sum = 0.0;
for(int i = 0; I < terms; i++){
int sign = pow(-1,i); //tìm dấu trước mỗi số hạng
double term = 1.0/(i*2+1);
sum += sign * term;
}
return sum*4;
}
The future is that is the thing that you used to get the result.
The promise is the thing that you used to set the result.
6.3 Promises and Exceptions
#include <exception>
double calculate_pi(int terms) {
double sum = 0.0;
if (terms < 1) {
throw runtime_error("Terms cannot be less than 1");
}
for (int i = 0; i < terms; i++) {
int sign = pow(-1, i);
double term = 1.0 / (i * 2 + 1);
sum += sign * term;
}
return sum * 4;
}
int main(){
promise<double> promise;
auto do_calculation = [&](int terms) {
try {
auto result = calculate_pi(terms);
promise.set_value(result);
}
catch (...) {
promise.set_exception(current_exception()); //promise set exception
}
};
thread t1(do_calculation, 10000);//10000 is pass argument to function do_calculation
future<double> future = promise.get_future();
try {
cout << setprecision(15) << future.get() << endl;
}
catch (const exception &e) {
cout << e.what() << endl;
}
t1.join();
return 0;
}
6.4 Packaged Tasks
When 6.3 has exception, code is too long -> use packaged
double calculate_pi(int terms)
{
double sum = 0.0;
if (terms < 1){
throw runtime_error("Terms cannot be less than 1");
}
for (int i = 0; i < terms; i++){
int sign = pow(-1, i);
double term = 1.0 / (i * 2 + 1);
sum += sign * term;
}
return sum * 4;
}
int main(){
packaged_task<double(int)> task1(calculate_pi);
// packaged_task<returnTypeOfFunction(dataTypeOfParameter)> task1(nameFunction);
future<double> future1 = task1.get_future();
thread t1(move(task1), 0);
//task1 do not accept to copy constructor -> use ref() or move() (move semantic, unique
pointer)
try {
double result = future1.get();
cout << setprecision(15) << result << endl;
}
catch (exception &e) {
cout << "ERROR! " << e.what() << endl;
}
t1.join();
return 0;
}
//if have exception it will print: ERROR! Terms cannot be less than 1 Commented [NHS22]: Ultimately: cuoi cung
7) Signalling
7.1 Waiting for Threads
int main() {
atomic<bool> ready = false;
thread t1([&](){
this_thread::sleep_for(chrono::milliseconds(2000));
ready = true;
});
t1.join();
while(!ready) {
this_thread::sleep_for(chrono::milliseconds(100));
}
cout << "ready " << ready << endl;
return 0;
}
7.2 Condition Variables (use unique_lock, unlock rồi sau đó notify condition to other thread use this
condition)
#include <condition_variable>
int main() {
condition_variable condition;
mutex mtx;
bool ready = false;
thread t1([&](){
this_thread::sleep_for(chrono::milliseconds(2000));
unique_lock<mutex> lock(mtx);
ready = true;
lock.unlock();
condition.notify_one();
});
unique_lock<mutex> lock(mtx); //argument truyền vào tương tự với trong thread
notify condition
while(!ready) {
condition.wait(lock);
}
cout << "ready " << ready << endl;
t1.join();
return 0;
}
//condition.notifu_one(), condition.notifu_all() ->thông báo đến một hoặc tất cả các thread đang
waiting for condition để wakeup và thực hiện statement
7.3 Checking Condition Shared Resources
Instead of having that loop, what we can do is this the second argument that we can supply to wait,
while(!ready) {
condition.wait(lock);
}
=> condition.wait(lock, [&](){ return ready; }); //return false -> continue wait and reverse
7.4 Blocking Queues
Queue is a first in first out in data structure,
-Tìm hiểu boost blocking queue
return 0;
}
8.6 Timing Code
2.Thread
-Each process can have multiThread
- Thread's share the resources that are allocated to the process.
- Another benefit of using threads is that they allow you to take advantage of multicore
processors.
- When you have multiple threads, it is possible for each thread to be running on a different
core and this is going to speed things up for your application.
QT Thread: Qt Concurrent, Qt ThreadPool, QThread
QThread:
- QThread::create: can use create method from Q thread class
- moveToThread: can move the objects to another thread
- Subclass QThread: can subclass QThread to create our own thread
1) QThread::create
- QThread::create does not give you event loop:
=> do not have loop to catch signals from User’s actions
=> emit signal finished when method returns
=> Any calls to isRunning() after method returns will return false
- Remember manage memory when create thread:
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
- Trong khi bắt 1 signal, nếu trong slot đó thực hiện một hành động quá lâu sẽ dẫn
đến block UI ->đưa action đó vào 1 thread khác để thực hiện
EX:
void counting1(int count){
for(int i{0} ; i < count ; i ++){
qDebug() << "Counting : " << i << "thread : " << QThread::currentThread();
}
}
void Widget::counting(){
for(int i{0} ; i < 10000 ; i ++){
qDebug() << "Counting method called : " << i <<
" thread :" << QThread::currentThread() << " id : " <<
QThread::currentThreadId() <<
"Thread is running : " << thread->isRunning();
}
}
void Widget::on_startButton_clicked(){
/*
//0.Freeze the UI
for(int i{0} ; i < 1000000 ; i ++){
qDebug() << " counting in ui thread..."<< "Counting : " << i <<
" thread :" << QThread::currentThread() << " id : " <<
QThread::currentThreadId();
}
//1 .Global function
/* thread = QThread::create(counting1,10000); */
connect(thread,&QThread::finished,thread,&QThread::deleteLater);
thread->start();
qDebug() << "Clicked on the start button";
}
2) moveToThread
Link: https://github.com/rutura/ThreadingIPCCode/tree/master/2.CreatingThreads/2-
3MoveToThread
Note: Give us an event loop by default
- Action done but thread is still running because there is an event loop running in the
thread
- To force the even loop, call the exit method on the thread
Second thread doCounting() đếm đến số cực lớn, for(int i(0), i < 1E8, i++), mỗi giá trị của i
sẽ emit currentCount(int value) sang mainThread nhận value và hiển thị lên UI
Nhưng số thông tin được emit sang mainThread quá lớn có thể dẫn đến block UI -> chia
nhỏ khoảng value để emit, cứ sau mỗi 10k đơn vị thì emit 1 lần -> số lần emit sẽ nhỏ ->
không block UI
workerThread->start();
}
3) Subclass QThread
Link: https://github.com/rutura/ThreadingIPCCode/tree/master/2.CreatingThreads/2-
4SubclassQThread
Note: - Don’t give us an event loop by default
- Create class derived from QThread class, you can create class derived from
QObject and then replace QObject by QThread
- We want to implement a method from the interface of thread, the way you do that,
which is pretty easy, is refactored.
- You right click on the class name here, you select refactor and you choose insert
virtual functions of base classes -> choose method
=>Method run() of QThread can give us an event loop here when we add statement exec();
EX:
WorkerThread * workerThread; // WorkerThread : public QThread
void WorkerThread::run() {
qDebug() << "Run method in thread : " << QThread::currentThread();
for(int i{0} ; i < 1000000001 ; i++){
/ * Only emit signal to send info to ui at 100000 intervals. UI can handle this.
* Otherwise it is going to freeze.*/
if((i%100000) == 0){
double percentage = ((i/1000000000.0)) * 100;
emit currentCount(QVariant::fromValue(percentage).toInt());
}
}
//Start event loop
exec() ;
}
//constructor class Widget
Widget::Widget(QWidget *parent) :QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
workerThread = new WorkerThread(this);
connect(workerThread, &WorkerThread::currentCount, this, &Widget::currentCount);
connect(workerThread,&QThread::started,[](){
qDebug() << "Thread started";
});
connect(workerThread,&QThread::finished,[](){
qDebug() << "Thread finished";
});
}
void Widget::on_startButton_clicked() {
workerThread->start();
}