Programming Tutorials
C++ Pthreads API
Table of Contents
Ever created a multi-threaded application? Using POSIX threads, called Pthreads you can create multi-threaded applications in almost any operating system. Pthreads give you the ability to control threads using C++. In this example, we will create a multi-threaded Matrix Multiplication Application.
I will be using pthreads, but you have the option of defining how I get our Matrix input, I used two files.
The following code can only run under Linux, though it will run under Windows with the required library like pthreads-w32.
Note: You may replace any qstring or q_ function with any function of your choice that does the same thing. For qstring, you can use string std class. For qDebug() you can replace it with std cout.
int main(int argc, char** argv){ int rc, status; if(argc > 3){ // Make sure there is 3 arguments. Matrix M = readMatrix(argv[1]); // Read the first input file, store it as Matrix M Matrix N = readMatrix(argv[2]); // Read the 2nd input file, store it as Matrix N QString Results(argv[3]); // Get output file name if(M.columns != N.rows){ // Cannot multiply matrices if the first one's columns are not equal to the second one's rows qDebug() << "Error: Cannot multiply these two matrices"; return 0; } pthread_t threads[(N.columns*M.rows)]; // Create a pthread_t pthread_attr_t attr; // Create attribute for pthread /* Initialize and set thread detached attribute */ pthread_attr_init(&attr); // initialize attribute pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); // make the attribute have JOINABLE option C = new Matrix; // Resulting C matrix C->rows = M.rows; // set result rows equal to the first matrix's row C->columns = N.columns; // set result column equal to the second matrix's column int t = 0; for(int r = 0; r < M.rows; r++){ // Loop rows 1st Matrix for(int c = 0; c < (N.columns); c++) { // Loop columns 2nd Matrix for(int i = 0; i < M.columns; i++){ // Loop columns 1st Matrix thread_data_array[t].thread_a_row[i] = M.V[r][i]; // Get first matrices row } for(int i = 0; i < N.rows; i++){ // Loop rows 2nd Matrix thread_data_array[t].thread_b_column[i] = N.V[i][c]; // Get second matrices rows } thread_data_array[t].thread_id = t; // set thread id thread_data_array[t].a = M.columns; // set first matrix's columns as a thread_data_array[t].b = N.rows; // set second matrix's rows as b rc = pthread_create(&threads[t], &attr, calcMatrix, (void *) &thread_data_array[t]); // Create Thread // Set pthread_t, then attribute attr, then calcMatrix our calculating function, give it the argument struct. if (rc) { // if there is an error qDebug() << "ERROR: " << rc << endl; exit(-1); } /* Free attribute and wait for the other threads */ t++; // increment thread } } pthread_attr_destroy(&attr); // destroy the attribute. int thr = 0; for(int r = 0; r < M.rows; r++){ for(int c = 0; c < N.columns; c++){ rc = pthread_join(threads[thr], (void **)&status); // Wait for the thread to end and join this function if (rc) { // check for error qDebug() << "ERROR join"; return 0; } C->V[r][c] = 0; for(int i = 0; i < N.rows; i++){ // Get the result of that one matrix multiplication C->V[r][c] = thread_data_array[thr].x; } thr++; } } // You can replace qDebug() with cout, and qPrintable isn't important. qDebug() << "Results recorded to " << Results << ": "; qDebug() << qPrintable(PrintMatrix(C)); // Print results on screen pthread_exit(NULL); // exit thread } else { qDebug() << "Please include the first matrix file, the second matrix file, and the output file in order for this program's arguments."; // in case there is an error } return 0; }
We first check for our arguments in command line, get out 2 Matrix classes.
Matrix class
Our Matrix classes look like this:
class Matrix { public: int V[30][30]; int rows; int columns; }; // a simple matrix class Matrix* C = 0;
The V represents our variables in rows and columns as a 2 dimensional array.
thread_data Structure, for Thread Arguments
Our thread_data class looks like this, it contains arguments for our threads, you can edit it as much as you want:
struct thread_data { int thread_id; int thread_a_row[30]; int thread_b_column[30]; int x; int a; int b; }; struct thread_data thread_data_array[20];
pthread_t structure
pthread_t threads[(N.columns*M.rows)];, this will create a number of threads that we will be using in our calculation function.
pthread_attr_t attribute structure
pthread_attr_t attr; // Create attribute for pthread /* Initialize and set thread detached attribute */ pthread_attr_init(&attr); // initialize attribute pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); // make the attribute have JOINABLE option
We set our attribute for our threads as PTHREAD_CREATE_JOINABLE, which means we can use pthread_join() to combine each thread with the main thread, and wait for it to do this. This allows us to sync our threads back to a single thread.
We create our Resulting Matrix C, which is our answer, and we add our arguments for each calcMatrix thread using thread_data_array.
pthread_create function
rc = pthread_create(&threads[t], &attr, calcMatrix, (void *) &thread_data_array[t]); // Create Thread
We create each thread, add our attribute, add our calcMatrix (which is a function not a variable), and our argument thread_data_array, which has all our variables.
pthread_attr_destroy(&attr); // destroy the attribute.
Free the attribute.
pthread_join thread function
rc = pthread_join(threads[thr], (void **)&status); // Wait for the thread to end and join this function
This will join each thread to the main thread, so that we are synced up.
C->V[r][c] = thread_data_array[thr].x;
Add our results into our resulting matrix class.
pthread_exit(NULL); // exit thread
Exit the thread
CalcMatrix function
Here's our calcMatrix function:
void *calcMatrix(void *threadarg) { // calculate Matrix multithreaded function int taskid; struct thread_data *my_data; sleep(1); // Sleep 1 ms to not overburden the CPU my_data = (struct thread_data *) threadarg; // Get the argument structure taskid = my_data->thread_id; my_data->x = 0; for(int i = 0; i < my_data->a; i++) { // loop through until a is finished my_data->x += my_data->thread_a_row[i]*my_data->thread_b_column[i]; // multiply and add together } pthread_exit(NULL); // exit thread }
calcMatrix is going to be provided to each thread to use as a function. It will grab our thread_data from arguments, multiply the variables. Exit the pthread.
Reminder
Remember to #include <pthread.h>


Post new comment