Thread Trong C++ Là Gì / Top 15 # Xem Nhiều Nhất & Mới Nhất 6/2023 # Top View | 2atlantic.edu.vn

Process, Thread, Multithread, Synchronize (C++11

A program is consist a one or more process!

Each process consist one or more thread!

Mỗi process có một tài nguyên và vùng nhớ xác định! Các thread trong cùng một process thì cùng nhau chia sẻ tài nguyên của process!

When working with thread maybe it gona defference between OS (Windows and Unix-Linux)!

Do thread working with the same memory so in case of multi thread thì sinh ra việc phải đồng bộ hóa (synchronize) các thread với nhau để tránh trường hợp đọc và ghi không đồng nhất vào trên 1 vùng nhớ (ví dụ như 1 biến hoặc 1 object nào đó!)

Ví dụ về multithread

/* Add this to .pro file QMAKE_CXXFLAGS += -std=c++0x -pthread LIBS += -pthread */ using namespace std; void printJob(char c, int count) { for (int i = 0; i < count; ++i) { cout << c; } } int main() { std::thread printStar (printJob, '*', 100); std::thread printDola (printJob, '$', 100); printDola.join(); printStar.join(); return 0; }

A: Do test quá nhỏ nên trên Ubuntu có thể không thấy, cứ cho test thật lớn vào thì sẽ xảy ra bất đồng bộ!

Một trong những kỹ thuật người ta hay sử dụng để đồng bộ hóa các thread là sử dụng mutex, mutex đã được hỗ trợ sẵn trên C++11!

Chỉ cần thêm một đoạn lock và unlock sử dụng biến mutex là có thể đồng bộ hóa được rồi

mutex locker; void printJob(char c, int count) { locker.lock(); for (int i = 0; i < count; ++i) { cout << c; } locker.unlock(); }

Ở đây ta gọi phần bao quanh bởi biến locker kia là Critical Section, đó là đoạn code mà chỉ cho phép tại 1 thời điểm chỉ cho phép 1 thread thực thi đoạn code đó! (Mục đích là ta muốn các ký tự in ra liên tục theo đúng thứ tự!)

Mutex là viết tắt của “Mutual Exclusion” là việc giải quyết trùng lặp!

Một trong các ví dụ phổ biến trong multithread là producer-consumer problem! Problem như sau: Một thread chuyên sản xuất, một thread chuyên tiêu thụ, tránh conflic khi mà sản phẩm đã hết mà khách hàng vẫn tiêu thụ!

/* Add this to .pro file QMAKE_CXXFLAGS += -std=c++0x -pthread LIBS += -pthread */ using namespace std; int productCount = 0; void producerProc() { while(true) { productCount += (1 + rand()%10); cout << "Producer produce " << productCount << endl; this_thread::sleep_for(chrono::milliseconds(rand() % 2000)); } } void consumerProc() { while(true) { productCount -= (1 + rand()%10); cout << "Consumer consume " << productCount << endl; this_thread::sleep_for(chrono::milliseconds(rand() % 2000)); } } int main() { thread producerThread(producerProc); thread consumerThread(consumerProc); producerThread.join(); consumerThread.join(); return 0; }

Như trên chưa làm gì để đồng bộ thì sẽ có trường hợp cusumer thực hiện việc trừ thành âm (<0) số product!

Như vậy việc giải quyết đồng bộ ở đây như thế nào?

Ta có thể nghĩ đến việc sử dụng mutex rồi lock vào ntn:

void producerProc() { while(true) { locker.lock(); productCount += (1 + rand()%10); cout << "Producer produce " << productCount << endl; this_thread::sleep_for(chrono::milliseconds(rand() % 2000)); locker.unlock(); } } void consumerProc() { while(true) { locker.lock(); productCount -= (1 + rand()%10); cout << "Consumer consume " << productCount << endl; this_thread::sleep_for(chrono::milliseconds(rand() % 2000)); locker.unlock(); } }

Như này thì các lệnh sẽ coi như là chạy lần lượt!

Ta có thể thấy sự khác nhau nếu như ta unlock biến mutex ở trước vào sau khi sleep!

/* Add this to .pro file QMAKE_CXXFLAGS += -std=c++0x -pthread LIBS += -pthread */ using namespace std; mutex mtx; int productCount = 0; void producerProc(string name) { while(true) { mtx.lock(); int k = (1 + rand()%100); productCount += k; cout << "++++++++++++ " << name << " + " << k << " : " << productCount << endl; mtx.unlock(); this_thread::sleep_for(chrono::milliseconds(100 + rand() % 100)); } } void consumerProc(string name) { while(true) { int take = (1 + rand()%100); mtx.lock(); if(take <= productCount) { productCount -= take; cout << name << " consume -" << take <<" : " << productCount << endl; }else{ cout << name << " can't take " << take << "!" << endl; } mtx.unlock(); this_thread::sleep_for(chrono::milliseconds(rand() % 100)); } } int main() { srand(time(NULL)); thread producerThread1(producerProc, "P1"); thread producerThread2(producerProc, "P2"); thread consumerThread1(consumerProc, "A"); thread consumerThread2(consumerProc, "B"); producerThread1.join(); producerThread2.join(); consumerThread1.join(); consumerThread2.join(); return 0; }

ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms681917(v=vs.85).aspx http://www.bogotobogo.com/cplusplus/multithreading_pthread.php

https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem http://108.61.119.12/multithreading-semaphores/ http://108.61.119.12/multithreading-producer-consumer-problem/

Related

C++ — ‘Const Static’ Có Nghĩa Là Gì Trong C Và C ++?

Dòng mã đó thực sự có thể xuất hiện trong một số bối cảnh khác nhau và mặc dù nó hoạt động gần giống nhau, có những khác biệt nhỏ.

Phạm vi không gian tên

static const int i = 0;

‘i‘ sẽ hiển thị trong mọi đơn vị dịch thuật bao gồm tiêu đề. Tuy nhiên, trừ khi bạn thực sự sử dụng địa chỉ của đối tượng (ví dụ: ‘&i‘), tôi khá chắc chắn rằng trình biên dịch sẽ coi ‘i‘ đơn giản là một loại an toàn 0. Khi có thêm hai đơn vị dịch thuật lấy ‘&i‘ thì địa chỉ sẽ khác nhau cho mỗi đơn vị dịch thuật.

static const int i = 0;

‘i‘ có liên kết bên trong và do đó không thể được tham chiếu từ bên ngoài đơn vị dịch thuật này. Tuy nhiên, một lần nữa trừ khi bạn sử dụng địa chỉ của nó, rất có thể nó sẽ được coi là loại an toàn 0.

Một điều đáng để chỉ ra, đó là tuyên bố sau:

const int i1 = 0;

là chính xác giống như static const int i = 0. Một biến trong một không gian tên được khai báo bằng const và không được khai báo rõ ràng bằng extern là hoàn toàn tĩnh. Nếu bạn nghĩ về điều này, ý định của ủy ban C++ là cho phép các biến const được khai báo trong các tệp tiêu đề mà không cần luôn luôn cần từ khóa static để tránh phá vỡ ODR.

Phạm vi lớp học

class A { public: static const int i = 0; };

Trong ví dụ trên, tiêu chuẩn chỉ định rõ ràng rằng ‘i‘ không cần phải được xác định nếu không yêu cầu địa chỉ của nó. Nói cách khác, nếu bạn chỉ sử dụng ‘i‘ là loại an toàn 0 thì trình biên dịch sẽ không định nghĩa nó. Một điểm khác biệt giữa các phiên bản lớp và không gian tên là địa chỉ của ‘i‘ (nếu được sử dụng trong hai đơn vị dịch thuật nhiều quặng hơn) sẽ giống nhau cho thành viên lớp. Địa chỉ được sử dụng, bạn phải có định nghĩa cho địa chỉ đó:

class A { public: static const int i = 0; }; #include "a.h" const int A::i;

Chuỗi Là Gì? Hàm Nhập Xuất Chuỗi Trong C/C++

Trong ngôn ngữ lập trình C không có kiểu dữ liệu chuỗi mà chuỗi trong C là một dãy các kí tự kiểu char. Một chuỗi trong C được đánh dấu kết thúc là ” (còn gọi là ký tự NULL trong bảng mã ASCII) và có độ dài tùy ý, điều này cũng có nghĩa chuỗi ký tự trong C là một mảng các ký tự char.

Các Biến Chuỗi

Chuỗi là mảng ký tự kết thúc bởi ký tự null (”).

Có thể gán các hằng chuỗi cho các biến chuỗi.

Hằng chuỗi là một chuỗi các ký tự nằm trong dấu nháy kép.

Ký tự null ” được tự động thêm vào biểu diễn bên trong của chuỗi.

Khi khai báo một biến chuỗi, hãy dành thêm một phần tử trống cho ký tự kết thúc.

Ví dụ: Trong chương trình, ta có khai báo:

Trong khai báo này, bộ nhớ sẽ cung cấp 12+1 bytes để lưu trữ nội dung của chuỗi ký tự Ten; byte cuối cùng lưu trữ ký tự ” để chấm dứt chuỗi.

Lưu ý:

Chiều dài tối đa của biến chuỗi là một hằng nguyên nằm trong khoảng từ 1 đến 255 bytes.

Chiều dài tối đa không nên khai báo thừa để tránh lãng phí bộ nhớ, nhưng cũng không nên khai báo thiếu.

Khai báo theo con trỏ Cú pháp

Ví dụ: Trong chương trình, ta có khai báo:

Trong khai báo này, bộ nhớ sẽ dành 2 byte để lưu trữ địa chỉ của biến con trỏ Ten đang chỉ đến, chưa cung cấp nơi để lưu trữ dữ liệu. Muốn có chỗ để lưu trữ dữ liệu, ta phải gọi đến hàm malloc() hoặc calloc() có trong “alloc.h”, sau đó mới gán dữ liệu cho biến.

Vừa khai báo vừa gán giá trị

Ví dụ:

void main() {

char Chuoi[]=”sinhvientot.net website chia se kien thuc” ;

printf(“Vua khai bao vua gan gia tri : %s”,Chuoi) ;

getch();

}

Lưu ý: Chuỗi được khai báo là một mảng các ký tự nên các thao tác trên mảng có thể áp dụng đối với chuỗi ký tự.

Không thể cộng, trừ, nhân, chia 2 chuỗi kí tự lại bằng phép toán đơn thuần. Tất cả những điều đó phải được làm bằng các hàm riêng lẽ. Ta có thể gán một chuỗi này bằng một chuỗi khác (strcpy), so sánh 2 chuỗi kí tự với nhau theo thứ tự từ điển (strcmp), cộng 2 chuỗi với nhau (strcat),…

Mọi hằng chuỗi đều được ngôn ngữ lập trình C lưu trữ như là một mảng các char và kết thúc bằng kí tự “”. Hơn nữa, một chuỗi trong chương trình chúng ta chỉ nhận được địa chỉ và chỉ đến đầu mảng lưu trữ. Việc truy xuất đến một hằng chuỗi đều được thực hiện qua một pointer chỉ đến mảng đó.

Nhập chuỗi từ bàn phím

Để nhập một chuỗi ký tự từ bàn phím, ta sử dụng hàm gets()

Ví dụ: char Ten[20];

Ta cũng có thể sử dụng hàm scanf() để nhập dữ liệu cho biến chuỗi, tuy nhiên lúc này ta chỉ có thể nhập được một chuỗi không có dấu khoảng trắng.

Ngoài ra, hàm cgets() (trong conio.h) cũng được sử dụng để nhập chuỗi.

void main() { char ten[50]; printf(“Nhap ten: “); /*Không có chỉ thị & vì ten chuỗi đã là một địa chỉ*/ scanf(“%s”,ten); printf(“Chao : %sn”,ten); getch(); }

void main() { char ten[30]; printf(“Nhap ten: “); flushall(); gets(ten); printf(“Chao :”); puts(ten); }

Xuất chuỗi lên màn hình

Để xuất một chuỗi (biểu thức chuỗi) lên màn hình, ta sử dụng hàm puts().

Cú pháp:

Ví dụ: Nhập vào một chuỗi và hiển thị trên màn hình chuỗi vừa nhập.

#include<string.h void main() { char Ten[12]; printf(“Nhap chuoi: “); gets(Ten); printf(“Chuoi vua nhap: “); puts(Ten); getch(); }

Ngoài ra, ta có thể sử dụng hàm printf(), cputs() (trong conio.h) để hiển thị chuỗi lên màn hình.

Xuất một chuỗi có xuống dòng sau khi xuất:

Xuất một chuỗi không xuống dòng sau khi xuất:

Lệnh Goto Trong C++ Là Lệnh Gì?

Các bạn thân mến! Hôm nay chúng ta sẽ tiếp tục tìm hiểu một lệnh nữa trong C++ đó là lệnh goto.

Vậy lệnh goto trong C++ là gì? Chúng ta sẽ cùng tìm hiểu trong nội dung sau đây nhé.

1. Lệnh goto trong C++

Câu lệnh goto trong C++ còn được gọi là câu lệnh nhảy. Lệnh goto được sử dụng để chuyển điều khiển sang phần khác của chương trình. Lệnh goto trong C++ nhảy đến nhãn được chỉ định.

Ví dụ như chương trình đang chạy đến ở dòng 300, chương trình gặp lệnh goto A (A là nhãn), nó sẽ nhãy lến dòng có nhãn là A và thực thi đoạn code ở đó.

Cú pháp

Cú pháp của lệnh goto trong C++ như sau:

goto label; . . . label: statement;

Hoặc

label: statement; . . . goto label;

Trong đó:

label: là nhãn do người lập trình định nghĩa, khi gặp lệnh goto label chương trình sẽ điều khiển đến label đó và thực thi đoạn code đó

statement: là các câu lệnh sẽ được thực thi

Lưu đồ hoạt động

Lưu đồ hoạt động của lệnh goto trong C++ như sau:

2. Ví dụ lệnh goto trong C++

Sau đây mình sẽ lấy một ví dụ đơn giản có sử dụng lệnh goto trong C++ đó là yêu cầu người dùng nhập một tuổi hợp lệ. ( Tuổi hợp lệ là tuối phải lớn hơn 0)

#include <iostream> using namespace std; int main() { TuoiHopLe: cout << "Nhap lai tuoi cua ban: " << endl; cout << "Tuoi ban la: "; int tuoi; cin >> tuoi; if (tuoi < 1){ goto TuoiHopLe; } else { cout << "Ban da nhap tuoi hop le" << endl; } }

Và kết quả sau khi chạy và thực hiện đoạn code trên như sau:

Mình xin giải thích luồng chạy của chương trình trên như sau:

Giả sử mình sẽ đánh số thứ tự các dòng của dòng code trên như dưới đây:

#include <iostream> using namespace std; int main() { 1 TuoiHopLe: 2 cout << “Nhap lai tuoi cua ban: ” << endl; 3 cout << “Tuoi ban la: “; 4 int tuoi; 5 cin >> tuoi; 6 if (tuoi < 1) { 7 goto TuoiHopLe; } else { 8 cout << “Ban da nhap tuoi hop le” << endl; } }

Nếu một chương trình bình thường sẽ chạy theo thứ tự từ dòng 1 đến dòng cuối cùng và kết thúc chương trình. Chương trình không thể nào quay lại dòng trước để thực thi một lần nữa. Nhưng đối với chương trình có sử dụng goto thì cho phép chương trình quay lại dòng trước để thực thi tiếp.

Quay lại ví dụ trên, chương trình gọi tới hàm main sẽ chạy từ dòng 1, 2, 3, 4, 5, đến dòng 6 kiểm tra điều kiện nếu đúng thì chạy đến dòng 7, ở dòng 7 này có lệnh goto có nhãn là TuoiHopLe, chương trình sẽ tìm dòng có nhãn là TuoiHopLe để chạy, ở ví dụ trên nhãn TuoiHopLe ở dòng 1 vì vậy chương trình quay lại dòng 1 để chạy rồi tiếp tục dòng 2, 3, 4, 5, đến dòng 6 lại kiếm tra điều kiện nếu đúng thì đến dòng 7 và tiếp tục trở về dòng 1, nếu điều kiện sai thì chạy xuống dòng 8 và kết thúc chương trình.

Tuy nhiên các bạn nên hạn chế sử dụng lệnh goto trong chương trình của mình. Chỉ nên sử dụng lệnh goto trong một số trường hợp đặc biệt bắt buộc phải sử dụng mà thôi.

Lý do mà mình khuyên các bạn hạn chế sử dụng lệnh goto trong chương trình của mình là vì những điểm bất lợi của lệnh goto trong C++ như sau:

nó làm cho logic chương trình rất phức tạp

sử dụng lệnh goto rất khó để bảo trì, sử dụng nhiều lệnh goto trong một chương trình là một cơn ác mộng đối với người chịu trách nhiệm bào trì chương trình.

có thể sử dụng các câu lệnh break và continue để tránh sử dụng lệnh goto

chương trình rất dể bị vô vòng lặp vô hạn

Mình có thể giải quyết ví dụ trên bằng vòng lặp do while và lệnh break trong C++ mà không cần dùng đến lệnh goto như sau:

#include <iostream> using namespace std; int main() { int tuoi; do { cout << "Nhap lai tuoi cua ban: " << endl; cout << "Tuoi ban la: "; cin >> tuoi; if (tuoi < 1){ } else { cout << "Ban da nhap tuoi hop le" << endl; break; } } while(tuoi < 1); }

3. Kết luận

Như vậy là chúng ta đã tìm hiểu xong lệnh goto trong C++ là gì rồi. Trong bài học này các bạn chỉ cần biết cách sử dụng của lệnh goto là như thế nào thôi, mình không khuyến khích các bạn sử dụng lệnh goto để giải quyết vấn đề nhé, chỉ thật sự sử dụng lệnh goto trong một số trường hợp đặc biệt bắt buộc phải sử dụng lênh goto thôi nhé. Mà trường hợp đặc biệt đó là gì thì mình cũng chưa biết vì đó giờ mình cũng chưa từng sử dụng lệnh goto trong chương trình của mình. 🙂

Như vậy mình sẽ kết thúc bài học này ở đây nhé. Cám ơn các bạn đã đọc bài viết.