Difference between revisions of "Pthread"
 (Created page with " =Links= *[https://computing.llnl.gov/tutorials/pthreads/ Tutorial on pthreads from Lawrence Livermore National Laboratory] (Good) *[https://www.yangyang.cloud/blog/2018/11/09...")  | 
				m (→Another example)  | 
				||
| (21 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
| + | =Using pthreads=  | ||
| + | ==Single thread example==  | ||
| + | The example below shows the syntax of pthreads. The thread doesn't do anything useful.  | ||
| + | *compile with: '''gcc pt1.c -o pt -pthread'''   | ||
| + | <source lang=c line>  | ||
| + | #include <stdio.h>  | ||
| + | #include <pthread.h>  | ||
| + | |||
| + | long counter;  | ||
| + | void * adder(void * data) {  | ||
| + |         for (int i=0; i < 1000000; i++)  | ||
| + |                 counter++;  | ||
| + | }  | ||
| + | |||
| + | int main(void) {  | ||
| + |         int i;  | ||
| + |         pthread_t pt;  | ||
| + | |||
| + |         counter=0;  | ||
| + |         pthread_create(&pt, NULL, adder, NULL);  | ||
| + |         pthread_join(pt, NULL);  | ||
| + |         printf("Value of counter is %ld\n", counter);  | ||
| + |         return(0);  | ||
| + | }  | ||
| + | </source>  | ||
| + | When creating a pthread with '''pthread_create()''' resources are allocated in the running process for the thread. '''pthread_join()''' waits for the thread to finish and releases its resources.  | ||
| + | |||
| + | ==Using multiple threads==  | ||
| + | Using multiple threads simultaneously can give [[shared resource race condition|race conditions]] as the code below shows when running.   | ||
| + | |||
| + | <source lang=c line>  | ||
| + | #include <stdio.h>  | ||
| + | #include <pthread.h>  | ||
| + | |||
| + | #define THREADS 2  | ||
| + | |||
| + | long counter;  | ||
| + | void * adder(void * data) {  | ||
| + |         for (int i=0; i < 100000000; i++)  | ||
| + |                 counter++;  | ||
| + | }  | ||
| + | |||
| + | int main(void) {  | ||
| + |         int i;  | ||
| + |         pthread_t pt[THREADS];  | ||
| + | |||
| + |         counter=0;  | ||
| + | |||
| + |         for (i=0; i < THREADS; i++) {  | ||
| + |                 pthread_create(&pt[i], NULL, adder, NULL);  | ||
| + |                 printf("Created thread %d\n",i+1);  | ||
| + |         }  | ||
| + | |||
| + |         for (i=0; i < THREADS; i++) {  | ||
| + |                 pthread_join(pt[i], NULL);  | ||
| + |                 printf("Joined thread %d\n",i+1);  | ||
| + |         }  | ||
| + | |||
| + |         printf("Value of counter is %ld\n", counter);  | ||
| + |         return(0);  | ||
| + | }  | ||
| + | </source>  | ||
| + | Running the code two times gave this output as example:  | ||
| + | *'''Note:''' The two wrong counter values.  | ||
| + | <source lang=bash>  | ||
| + | heth@emb3:~/bin$ pt2  | ||
| + | Created thread 1  | ||
| + | Created thread 2  | ||
| + | Joined thread 1  | ||
| + | Joined thread 2  | ||
| + | Value of counter is 1032119  | ||
| + | heth@emb3:~/bin$ pt2  | ||
| + | Created thread 1  | ||
| + | Created thread 2  | ||
| + | Joined thread 1  | ||
| + | Joined thread 2  | ||
| + | Value of counter is 1081391  | ||
| + | </source>  | ||
| + | |||
| + | ==Using mutexes==  | ||
| + | A [[Mutex]] or Mutual exclusion is a lock that can be owned by only thread at a time. When unlocked another thread can lock it.   | ||
| + | <source lang=c>  | ||
| + | #include <stdio.h>  | ||
| + | #include <pthread.h>  | ||
| + | |||
| + | #define THREADS 2  | ||
| + | |||
| + | long counter;  | ||
| + | pthread_mutex_t mutex;  | ||
| + | |||
| + | void * adder(void * data) {  | ||
| + |         for (int i=0; i < 1000000; i++) {  | ||
| + |                 pthread_mutex_lock(&mutex) ;  | ||
| + |                 counter++;  | ||
| + |                 pthread_mutex_unlock(&mutex);  | ||
| + |         }  | ||
| + | }  | ||
| + | |||
| + | int main(void) {  | ||
| + |         int i;  | ||
| + |         pthread_t pt[THREADS];  | ||
| + | |||
| + | |||
| + |         counter=0;  | ||
| + |         if (pthread_mutex_init(&mutex, NULL) != 0)  | ||
| + |                 printf("Mutex init failed\n");  | ||
| + | |||
| + |         for (i=0; i < THREADS; i++) {  | ||
| + |                 pthread_create(&pt[i], NULL, adder, NULL);  | ||
| + |                 printf("Created thread %d\n",i+1);  | ||
| + |         }  | ||
| + | |||
| + |         for (i=0; i < THREADS; i++) {  | ||
| + |                 pthread_join(pt[i], NULL);  | ||
| + |                 printf("Joined thread %d\n",i+1);  | ||
| + |         }  | ||
| + | |||
| + |         printf("Value of counter is %ld\n", counter);  | ||
| + |         return(0);  | ||
| + | }  | ||
| + | </source>  | ||
| + | The output from this example is accurate:  | ||
| + | <source lang=bash>  | ||
| + | heth@emb3:~/bin$ pt1_mutex  | ||
| + | Created thread 1  | ||
| + | Created thread 2   | ||
| + | Joined thread 1  | ||
| + | Joined thread 2  | ||
| + | Value of counter is 2000000  | ||
| + | </source>  | ||
| + | |||
| + | =Releasing resources used by a pthread=  | ||
| + | To release the resources - memory for stack and householding - it is necessary to call either [https://www.man7.org/linux/man-pages/man3/pthread_detach.3.html pthread_detach] or [https://www.man7.org/linux/man-pages/man3/pthread_join.3.html pthread_join]. The resources are not released when the pthread exits without one of these calls.  | ||
| + | *[https://www.man7.org/linux/man-pages/man3/pthread_join.3.html pthread_join] is called by the process or another thread.  | ||
| + | *[https://www.man7.org/linux/man-pages/man3/pthread_detach.3.html pthread_detach] is typically called by the thread itself to release the resources when the thread terminates  | ||
| + | =See threads of a running process=  | ||
| + | ==Using ps==  | ||
| + | To see the thread ids - TID - of a running process use '''ps -T''' or filter as below   | ||
| + | <source lang=bash>  | ||
| + | heth@emb3:~/bin/process-and-thread/thread$ ps -L xo pid,ppid,tid,cmd | grep -P '(pt2|CMD)'  | ||
| + |     PID     TID CMD  | ||
| + | 3516974 3516974 ./pt2_mutex  | ||
| + | 3516974 3516975 ./pt2_mutex  | ||
| + | 3516974 3516976 ./pt2_mutex  | ||
| + | 3522329 3522329 grep --color=auto -P (pt2|CMD)  | ||
| + | </source>  | ||
| + | ;NOTE:  | ||
| + | : Some confusion about thread ID - TID and other abbreviations - in the man page for '''ps''' the value of TID can also appear as lwp or spid  | ||
| + | |||
| + | =Another example=  | ||
| + | The example below shows the use of pthread_cancel. The script in the end shows the PID - Process ID - of the process and TID - Thread ID - of the threads.  | ||
| + | |||
| + | <source lang=c>  | ||
| + | #include <stdio.h>  | ||
| + | #include <pthread.h>  | ||
| + | #include <unistd.h>  | ||
| + | |||
| + | // Compile with: gcc thread3.c -o thread3 -pthread -lc  | ||
| + | //   Note: \x1b[ - are vt100 escape sequences. Google on it  | ||
| + | //   Note: printf is reasonable treadsafe (see man  page)  | ||
| + | |||
| + | void * thread_0(void * data) {  | ||
| + | |||
| + |         static unsigned int count=0;  | ||
| + | |||
| + |         while(count <  200) {  | ||
| + |                 count++;  | ||
| + |                 printf("\x1b[2;1HThread_0 count = %10d", count);  | ||
| + |                 fflush(stdout);  | ||
| + |                 usleep(100000);  | ||
| + |         }  | ||
| + | }  | ||
| + | |||
| + | void * thread_1(void * data) {  | ||
| + | |||
| + |         static unsigned int count=0;  | ||
| + | |||
| + |         while(1) {  | ||
| + |                 count++;  | ||
| + |                 printf("\x1b[3;1HThread_1 count = %10d", count);  | ||
| + |                 fflush(stdout);  | ||
| + |                 pthread_testcancel();  | ||
| + |                 usleep(10000);  | ||
| + |         }  | ||
| + | }  | ||
| + | |||
| + | void * thread_2(void * data) {  | ||
| + | |||
| + |         static unsigned int count=0;  | ||
| + | |||
| + |         while(1) {  | ||
| + |                 count++;  | ||
| + |                 printf("\x1b[4;1HThread_2 count = %10d", count);  | ||
| + |                 fflush(stdout);  | ||
| + |                 pthread_testcancel();  | ||
| + |                 usleep(1000000);  | ||
| + |         }  | ||
| + | }  | ||
| + | |||
| + | int main(void) {  | ||
| + |         int i;  | ||
| + | |||
| + |         pthread_t pt[3];  | ||
| + | |||
| + | |||
| + |         printf("\x1b[2J");  | ||
| + |         fflush(stdout);  | ||
| + |         pthread_create(&pt[0], NULL, thread_0, NULL);  | ||
| + |         pthread_create(&pt[1], NULL, thread_1, NULL);  | ||
| + |         pthread_create(&pt[2], NULL, thread_2, NULL);  | ||
| + | |||
| + |         pthread_join(pt[0], NULL);  | ||
| + |         printf("\x01b[8;1HJoined thread_0\n");  | ||
| + |         usleep(3000000);  | ||
| + | |||
| + |         pthread_cancel(pt[1]);  | ||
| + |         printf("\x01b[9;1HCancelled  thread_1\n");  | ||
| + |         usleep(3000000);  | ||
| + | |||
| + |         pthread_cancel(pt[2]);  | ||
| + |         printf("\x01b[10;1HCancelled  thread_2\n");  | ||
| + |         usleep(3000000);  | ||
| + | |||
| + |         printf("\x01b[11;1HExiting\n");  | ||
| + | |||
| + |         return(0);  | ||
| + | }  | ||
| + | </source>  | ||
| + | Run the following script in another terminal to see running threads  | ||
| + | <source lang=bash>  | ||
| + | while :; do clear; ps -L xo pid,ppid,tid,cmd | grep -P '(thread3|CMD)'; sleep 0.5; done  | ||
| + | </source>  | ||
=Links=  | =Links=  | ||
| − | *[https://  | + | *[https://www.cs.cmu.edu/afs/cs/academic/class/15492-f07/www/pthreads.html Tutorial on pthreads from Carnegie Mellon University] (Good)  | 
*[https://www.yangyang.cloud/blog/2018/11/09/worker-pool-with-eventfd/ Pthread example: Worker poll with eventfd]  | *[https://www.yangyang.cloud/blog/2018/11/09/worker-pool-with-eventfd/ Pthread example: Worker poll with eventfd]  | ||
| − | [[Category:C]][[Category:Linux]]  | + | *[https://www.hpl.hp.com/techreports/2004/HPL-2004-209.pdf HP - Threads in C - are they safe]  | 
| + | *[https://elinux.org/images/1/1c/Ben-Yossef-GoodBadUgly.pdf Pthreads PPT]  | ||
| + | |||
| + | |||
| + | [[Category:C]][[Category:Linux]][[Category:GNU]]  | ||
Latest revision as of 10:05, 19 April 2024
Contents
Using pthreads
Single thread example
The example below shows the syntax of pthreads. The thread doesn't do anything useful.
- compile with: gcc pt1.c -o pt -pthread
 
 1 #include <stdio.h>
 2 #include <pthread.h>
 3 
 4 long counter;
 5 void * adder(void * data) {
 6         for (int i=0; i < 1000000; i++)
 7                 counter++;
 8 }
 9 
10 int main(void) {
11         int i;
12         pthread_t pt;
13 
14         counter=0;
15         pthread_create(&pt, NULL, adder, NULL);
16         pthread_join(pt, NULL);
17         printf("Value of counter is %ld\n", counter);
18         return(0);
19 }
When creating a pthread with pthread_create() resources are allocated in the running process for the thread. pthread_join() waits for the thread to finish and releases its resources.
Using multiple threads
Using multiple threads simultaneously can give race conditions as the code below shows when running.
 1 #include <stdio.h>
 2 #include <pthread.h>
 3 
 4 #define THREADS 2
 5 
 6 long counter;
 7 void * adder(void * data) {
 8         for (int i=0; i < 100000000; i++)
 9                 counter++;
10 }
11 
12 int main(void) {
13         int i;
14         pthread_t pt[THREADS];
15 
16         counter=0;
17 
18         for (i=0; i < THREADS; i++) {
19                 pthread_create(&pt[i], NULL, adder, NULL);
20                 printf("Created thread %d\n",i+1);
21         }
22 
23         for (i=0; i < THREADS; i++) {
24                 pthread_join(pt[i], NULL);
25                 printf("Joined thread %d\n",i+1);
26         }
27 
28         printf("Value of counter is %ld\n", counter);
29         return(0);
30 }
Running the code two times gave this output as example:
- Note: The two wrong counter values.
 
heth@emb3:~/bin$ pt2
Created thread 1
Created thread 2
Joined thread 1
Joined thread 2
Value of counter is 1032119
heth@emb3:~/bin$ pt2
Created thread 1
Created thread 2
Joined thread 1
Joined thread 2
Value of counter is 1081391
Using mutexes
A Mutex or Mutual exclusion is a lock that can be owned by only thread at a time. When unlocked another thread can lock it.
#include <stdio.h>
#include <pthread.h>
#define THREADS 2
long counter;
pthread_mutex_t mutex;
void * adder(void * data) {
        for (int i=0; i < 1000000; i++) {
                pthread_mutex_lock(&mutex) ;
                counter++;
                pthread_mutex_unlock(&mutex);
        }
}
int main(void) {
        int i;
        pthread_t pt[THREADS];
        counter=0;
        if (pthread_mutex_init(&mutex, NULL) != 0)
                printf("Mutex init failed\n");
        for (i=0; i < THREADS; i++) {
                pthread_create(&pt[i], NULL, adder, NULL);
                printf("Created thread %d\n",i+1);
        }
        for (i=0; i < THREADS; i++) {
                pthread_join(pt[i], NULL);
                printf("Joined thread %d\n",i+1);
        }
        printf("Value of counter is %ld\n", counter);
        return(0);
}
The output from this example is accurate:
heth@emb3:~/bin$ pt1_mutex
Created thread 1
Created thread 2 
Joined thread 1
Joined thread 2
Value of counter is 2000000
Releasing resources used by a pthread
To release the resources - memory for stack and householding - it is necessary to call either pthread_detach or pthread_join. The resources are not released when the pthread exits without one of these calls.
- pthread_join is called by the process or another thread.
 - pthread_detach is typically called by the thread itself to release the resources when the thread terminates
 
See threads of a running process
Using ps
To see the thread ids - TID - of a running process use ps -T or filter as below
heth@emb3:~/bin/process-and-thread/thread$ ps -L xo pid,ppid,tid,cmd | grep -P '(pt2|CMD)'
    PID     TID CMD
3516974 3516974 ./pt2_mutex
3516974 3516975 ./pt2_mutex
3516974 3516976 ./pt2_mutex
3522329 3522329 grep --color=auto -P (pt2|CMD)
- NOTE
 - Some confusion about thread ID - TID and other abbreviations - in the man page for ps the value of TID can also appear as lwp or spid
 
Another example
The example below shows the use of pthread_cancel. The script in the end shows the PID - Process ID - of the process and TID - Thread ID - of the threads.
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// Compile with: gcc thread3.c -o thread3 -pthread -lc
//   Note: \x1b[ - are vt100 escape sequences. Google on it
//   Note: printf is reasonable treadsafe (see man  page)
void * thread_0(void * data) {
        static unsigned int count=0;
        while(count <  200) {
                count++;
                printf("\x1b[2;1HThread_0 count = %10d", count);
                fflush(stdout);
                usleep(100000);
        }
}
void * thread_1(void * data) {
        static unsigned int count=0;
        while(1) {
                count++;
                printf("\x1b[3;1HThread_1 count = %10d", count);
                fflush(stdout);
                pthread_testcancel();
                usleep(10000);
        }
}
void * thread_2(void * data) {
        static unsigned int count=0;
        while(1) {
                count++;
                printf("\x1b[4;1HThread_2 count = %10d", count);
                fflush(stdout);
                pthread_testcancel();
                usleep(1000000);
        }
}
int main(void) {
        int i;
        pthread_t pt[3];
        printf("\x1b[2J");
        fflush(stdout);
        pthread_create(&pt[0], NULL, thread_0, NULL);
        pthread_create(&pt[1], NULL, thread_1, NULL);
        pthread_create(&pt[2], NULL, thread_2, NULL);
        pthread_join(pt[0], NULL);
        printf("\x01b[8;1HJoined thread_0\n");
        usleep(3000000);
        pthread_cancel(pt[1]);
        printf("\x01b[9;1HCancelled  thread_1\n");
        usleep(3000000);
        pthread_cancel(pt[2]);
        printf("\x01b[10;1HCancelled  thread_2\n");
        usleep(3000000);
        printf("\x01b[11;1HExiting\n");
        return(0);
}
Run the following script in another terminal to see running threads
while :; do clear; ps -L xo pid,ppid,tid,cmd | grep -P '(thread3|CMD)'; sleep 0.5; done