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