created by Frank
What is a thread?
Like processes, threads are a mechanism that permits an application to perform multiple tasks concurrently.
threads in process |
thread stack in virtual memory address |
thread life |
Creating a thread
using the following functions to create a thread:
- #include <pthread.h><pthread .h="">
- /*create a thread*/
- int pthread_create(pthread_t *thread, const pthread_attr_t *attr, </pthread>
- void *(*start_routine) (void *), void *arg);
- /*wait a thread finishing and get return value*/
- int pthread_join(pthread_t *thread, void **retval);
- /*thread returns value*/
- int pthread_exit(void *retval);
example
- #include <stdio.h>
- #include <pthread.h>
- static void *threadFunc(void *arg)
- {
- char *s = (char *) arg;
- printf("%s", s);
- pthread_exit("thread finishes");
- }
- int main(int argc, char *argv[])
- {
- pthread_t t1;
- void *res;
- int s;
- s = pthread_create(&t1, NULL, threadFunc, "Hello world\n");
- if (s != 0)
- printf("pthread_create");
- printf("Message from main()\n");
- s = pthread_join(t1, &res);
- if (s != 0)
- printf("pthread_join");
- printf("Thread returned %s\n", (char *) res);
- exit(EXIT_SUCCESS);
- }
some data type defined in pthread.h header file
data types in pthread.h |
- To the Linux kernel, there is no concept of a thread
- Linux implements all threads as standard processes
- Child process from the parent is that the child has only one thread, the fork() call clones just the thread which executed it.
Thread attributes
with attributes, we have many options to control threadsthe thread attributes object
1
| pthread_attr_t attr; |
1
2
3
4
5
6
7
8
9
10
11
| typedef struct { int __detachstate; int __schedpolicy; struct sched_param __schedparam; int __inheritsched; int __scope; size_t __guardsize; int __stackaddr_set; void *__stackaddr; unsigned long int __stacksize; } pthread_attr_t; |
firstly, we must initialize the attr
1
| pthread_attr_init(pthread_attr_t *attr); |
and, we must destroy the attr after using
1
| pthread_attr_destroy(pthread_attr_t *attr); |
Detachedstate
sometimes we don't want to a thread join to main thread, so we use detachedstate attribute.
There are 2 methods to detach a thread. After detaching, the thread can not join to main thread
Method 1; use pthread_detach(pthread_t thread);
- call in main thread: pthread_detach(thread);
- call in the detach thread: pthread_detach(thread_self());
if we use this system call, after that calling thread_join will return error
example
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <pthread.h>
- void *thread_function(void *arg);
- char message[] = "Hello World";
- int thread_finished = 0;
- void *thread_result;
- int main() {
- int res, s;
- pthread_t a_thread;
- pthread_attr_t thread_attr;
- res = pthread_create(&a_thread, &thread_attr, thread_function, (void *)message);
- pthread_detach(a_thread);
- s = pthread_join(a_thread,&thread_result);
- if(s != 0)
- printf("error to join\n");
- (void)pthread_attr_destroy(&thread_attr);
- while(!thread_finished) {
- printf("Waiting for thread to say it's finished...\n");
- sleep(1);
- }
- printf("Other thread finished, bye!\n");
- exit(EXIT_SUCCESS);
- }
- void *thread_function(void *arg)
- {
- //pthread_detach(pthread_self());
- printf("thread_function is running. Argument was %s\n", (char *)arg);
- sleep(4);
- printf("Second thread setting finished flag, and exiting now\n");
- thread_finished = 1;
- pthread_exit(NULL);
- }
Method 2:
there are 2 flags: PTHREAD_CREATE_JOINABLE and PTHREAD_CREATE_DETACHED of
use the call to set detach state for attr object:
1
| int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); |
1
| int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate); |
example:
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <pthread.h>
- void *thread_function(void *arg);
- char message[] = "Hello World";
- int thread_finished = 0;
- void *thread_result;
- int main() {
- int res;
- pthread_t a_thread;
- pthread_attr_t thread_attr;
- res = pthread_attr_init(&thread_attr);
- res = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
- res = pthread_create(&a_thread, &thread_attr, thread_function, (void *)message);
- pthread_join(a_thread,&thread_result);
- (void)pthread_attr_destroy(&thread_attr);
- while(!thread_finished) {
- printf("Waiting for thread to say it's finished...\n");
- sleep(1);
- }
- printf("Other thread finished, bye!\n");
- exit(EXIT_SUCCESS);
- }
- void *thread_function(void *arg)
- {
- printf("thread_function is running. Argument was %s\n", (char *)arg);
- sleep(4);
- printf("Second thread setting finished flag, and exiting now\n");
- thread_finished = 1;
- pthread_exit(NULL);
- }
Schedpolicy
This controls how threads are scheduled.
Use 2 functions:
1
2
| int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); int pthread_attr_getschedpolicy( const pthread_attr_t *attr, int *policy); |
- SCHED_OTHER: default
- SCHED_RR: (only running process with super permission, real time scheduling), uses the round-robin scheduling scheme
- SCHED_FIFO: (only running process with super permission, real time scheduling), uses the firsts in - first out scheduling scheme
Schedparam
In schedpolicy, if we choose policy is SCHED_OTHER, this is a parter to schedpolicy and allows control over the scheduling of threads
- int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
- int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
- where:
- const struct sched_param{
- int sched_priority;
- }
Inherisched
use 2 functions:
1
2
| int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit); int pthread_attr_getinheritsched( const pthread_attr_t *attr, int *inherit); |
2 flags of inherit:
- PTHREAD_EXPLICIT_SCHED (default): scheduling is explicitly set by the attributes
- PTHREAD_INHERIT_SCHED: a new thread will instead use the parametters that its creator thread was using
- /* pthreads_sched_test.c */
- #include <pthread.h><pthread .h="">
- #include <stdio.h><stdio .h="">
- #include <stdlib.h><stdlib .h="">
- #include <errno.h><unistd .h="">
- #include <unistd.h><errno .h="">
- #define handle_error_en(en, msg) \
- do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
- static void
- usage(char *prog_name, char *msg)
- {
- if (msg != NULL)
- fputs(msg, stderr);
- fprintf(stderr, "Usage: %s [options]\n", prog_name);
- fprintf(stderr, "Options are:\n");
- #define fpe(msg) fprintf(stderr, "\t%s", msg); /* Shorter */
- fpe("-a<policy><prio> Set scheduling policy and priority in\n");
- fpe(" thread attributes object\n");
- fpe(" <policy> can be\n");
- fpe(" f SCHED_FIFO\n");
- fpe(" r SCHED_RR\n");
- fpe(" o SCHED_OTHER\n");
- fpe("-A Use default thread attributes object\n");
- fpe("-i {e|i} Set inherit scheduler attribute to\n");
- fpe(" 'explicit' or 'inherit'\n");
- fpe("-m<policy><prio> Set scheduling policy and priority on\n");
- fpe(" main thread before pthread_create() call\n\n");
- exit(EXIT_FAILURE);
- }
- static int
- get_policy(char p, int *policy)
- {
- switch (p) {
- case 'f': *policy = SCHED_FIFO; return 1;
- case 'r': *policy = SCHED_RR; return 1;
- case 'o': *policy = SCHED_OTHER; return 1;
- default: return 0;
- }
- }
- static void
- display_sched_attr(int policy, struct sched_param *param)
- {
- printf(" policy=%s, priority=%d\n",
- (policy == SCHED_FIFO) ? "SCHED_FIFO" :
- (policy == SCHED_RR) ? "SCHED_RR" :
- (policy == SCHED_OTHER) ? "SCHED_OTHER" :
- "???",
- param->sched_priority);
- }
- static void
- display_thread_sched_attr(char *msg)
- {
- int policy, s;
- struct sched_param param;
- s = pthread_getschedparam(pthread_self(), &policy, ¶m);
- if (s != 0)
- handle_error_en(s, "pthread_getschedparam");
- printf("%s\n", msg);
- display_sched_attr(policy, ¶m);
- }
- static void *
- thread_start(void *arg)
- {
- display_thread_sched_attr("Scheduler attributes of new thread");
- return NULL;
- }
- int
- main(int argc, char *argv[])
- {
- int s, opt, inheritsched, use_null_attrib, policy;
- pthread_t thread;
- pthread_attr_t attr;
- pthread_attr_t *attrp;
- char *attr_sched_str, *main_sched_str, *inheritsched_str;
- struct sched_param param;
- /* Process command-line options */
- use_null_attrib = 0;
- attr_sched_str = NULL;
- main_sched_str = NULL;
- inheritsched_str = NULL;
- while ((opt = getopt(argc, argv, "a:Ai:m:")) != -1) {
- switch (opt) {
- case 'a': attr_sched_str = optarg; break;
- case 'A': use_null_attrib = 1; break;
- case 'i': inheritsched_str = optarg; break;
- case 'm': main_sched_str = optarg; break;
- default: usage(argv[0], "Unrecognized option\n");
- }
- }
- printf("%s %s %s\n", main_sched_str, attr_sched_str, inheritsched_str);
- if (use_null_attrib && (inheritsched_str != NULL || attr_sched_str != NULL))
- usage(argv[0], "Can't specify -A with -i or -a\n");
- /* Optionally set scheduling attributes of main thread,
- and display the attributes */
- if (main_sched_str != NULL) {
- if (!get_policy(main_sched_str[0], &policy))
- usage(argv[0], "Bad policy for main thread (-m)\n");
- param.sched_priority = strtol(&main_sched_str[1], NULL, 0);
- s = pthread_setschedparam(pthread_self(), policy, ¶m);
- if (s != 0)
- handle_error_en(s, "pthread_setschedparam");
- }
- display_thread_sched_attr("Scheduler settings of main thread");
- printf("\n");
- /* Initialize thread attributes object according to options */
- attrp = NULL;
- if (!use_null_attrib) {
- s = pthread_attr_init(&attr);
- if (s != 0)
- handle_error_en(s, "pthread_attr_init");
- attrp = &attr;
- }
- if (inheritsched_str != NULL) {
- if (inheritsched_str[0] == 'e')
- inheritsched = PTHREAD_EXPLICIT_SCHED;
- else if (inheritsched_str[0] == 'i')
- inheritsched = PTHREAD_INHERIT_SCHED;
- else
- usage(argv[0], "Value for -i must be 'e' or 'i'\n");
- s = pthread_attr_setinheritsched(&attr, inheritsched);
- if (s != 0)
- handle_error_en(s, "pthread_attr_setinheritsched");
- }
- if (attr_sched_str != NULL) {
- if (!get_policy(attr_sched_str[0], &policy))
- usage(argv[0],
- "Bad policy for 'attr' (-a)\n");
- param.sched_priority = strtol(&attr_sched_str[1], NULL, 0);
- s = pthread_attr_setschedpolicy(&attr, policy);
- if (s != 0)
- handle_error_en(s, "pthread_attr_setschedpolicy");
- s = pthread_attr_setschedparam(&attr, ¶m);
- if (s != 0)
- handle_error_en(s, "pthread_attr_setschedparam");
- }
- /* If we initialized a thread attributes object, display
- the scheduling attributes that were set in the object */
- if (attrp != NULL) {
- s = pthread_attr_getschedparam(&attr, ¶m);
- if (s != 0)
- handle_error_en(s, "pthread_attr_getschedparam");
- s = pthread_attr_getschedpolicy(&attr, &policy);
- if (s != 0)
- handle_error_en(s, "pthread_attr_getschedpolicy");
- printf("Scheduler settings in 'attr'\n");
- display_sched_attr(policy, ¶m);
- s = pthread_attr_getinheritsched(&attr, &inheritsched);
- printf(" inheritsched is %s\n",
- (inheritsched == PTHREAD_INHERIT_SCHED) ? "INHERIT" :
- (inheritsched == PTHREAD_EXPLICIT_SCHED) ? "EXPLICIT" :
- "???");
- printf("\n");
- }
- /* Create a thread that will display its scheduling attributes */
- s = pthread_create(&thread, attrp, &thread_start, NULL);
- s = pthread_join(thread, NULL);
- if (s != 0)
- handle_error_en(s, "pthread_join");
- exit(EXIT_SUCCESS);
- }
- </prio></policy></policy></prio></policy></errno></unistd></stdlib></stdio></pthread>
Scope
This attribute controls how scheduling of a thread is calculated. Since Linux only currently supports the value PTHREAD_SCOPE_SYSTEM, we will not look at this further here
Stacksize
Linux implements threads with a large amount of stack by default, so the feature is generally redundant on Linux and consequently not implemented.
To set the stacksize of thread, use:
To set the stacksize of thread, use:
1
| int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); |
To get the stacksize of thread, use
1
2
| $ulimit -s 8192 # The stack size limit is 8 MB |
1
| int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize); |
Thread cancellation
the function to request a thread to terminate
- int pthread_cancel(pthread_t thread);
- //set cancel state
- int pthread_setcancelstate(int state, int *oldstate);
- //states:
- PTHREAD_CANCEL_ENABLE: default
- PTHREAD_CANCEL_DISABLE: cancellation is disable
- //set cancel type
- int pthread_setcanceltype(int type, int *oldtype);
- //types:
- PTHREAD_CANCEL_ASYNCHRONOUS: cancel immediatetly
- PTHREAD_CANCEL_DEFERRER: wait until the cancellation point excution.
cancellation points |
Another method to create a cancellation point: void pthread_testcancel(void);
Cleanup handlers:
1
2
3
4
| #include <pthread.h><pthread .h=""> void pthread_cleanup_push( void (*routine)( void *), void *arg); void pthread_cleanup_pop( int execute); </pthread> |
- #include <stdio.h>
- #include <pthread.h>
- void *threadFunc(void *arg)
- {
- int j; int i=0;
- //pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
- //pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
- //pthread_setcanceltype(PTHREAD_CANCEL_DEFERRER, NULL);
- printf("new thread start\n"); //may be a cancellation point
- for (j = 1; ; ++j)
- {
- i++;
- //pthread_testcancel();
- }
- printf("Loop %d\n", j); //may be a cancellation point
- sleep(1); //a cancellation point
- return NULL;
- }
- int main(int argc, char *argv[])
- {
- pthread_t thr;
- int s;
- void *res;
- pthread_create(&thr, NULL, &threadFunc, NULL);
- sleep(3);
- pthread_cancel(thr);
- pthread_join(thr, &res);
- if(res == PTHREAD_CANCELED)
- printf("thread was cancel\n");
- else printf("thread was not canceled (should not happen!)\n");
- return 0;
- }
Thread synchronization
Mutex
Statically allocated mutexes
1
| pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; |
Dynamically allocated mutexes
1
2
3
4
| pthread_mutex_t mtx; pthread_mutexattr_t attr; int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); |
Locking and unlocking a mutex
- /*lock mutex, blocking*/
- int pthread_mutex_lock(pthread_mutex_t *mtx);
- /*unlock mutex*/
- int pthread_mutex_unlock(pthread_mutex_t *mtx);
- /*lock mutex, non-blocking*/
- int pthread_mutex_trylock(pthread_mutex_t *mtx);
- /*lock mutex, timeout*/
- int pthread_mutex_timelock(pthread_mutex_t *mtx);
Example:
- #include <stdio.h>
- #include <pthread.h>
- pthread_mutex_t mutex;
- int count;
- void* func(void *arg) {
- pthread_mutex_lock(&mutex);
- printf("This is thread %d\n",*(int*)arg);
- printf("count = %d\n", count++);
- sleep(1);
- pthread_mutex_unlock(&mutex);
- }
- int main(int argc, char **argv)
- {
- int i;
- pthread_t tid[5];
- count=0;
- pthread_mutex_init(&mutex,NULL);
- for(i=0; i < 5; i++)
- {
- pthread_create(&tid[i],NULL,func, (void*)&tid[i]);
- }
- for(i=0; i < 5; i++)
- {
- pthread_join(&tid[i],NULL);
- }
- return 0;
- }
Performance of mutexes
Programs without mutexes is faster programs using mutexes, but in the typical case, a thread would spend much more time doing other work, and perform relatively fewer mutex lock and unlock operations, so that the performance impact of using a mutex is not significant in most applications.
Mutex deadlock
mutex deadlock |
Condition variable
A condition variable allows one thread to inform other threads about changes in the state of a shared variable (or other shared resource) and allows the other threads to wait (block) for such notification
- /*statically*/
- pthread_cond_t cond= PTHREAD_COND_INITIALIZER;
- /*dynamically*/
- pthread_cond_t cond;
- pthread_conattr_t attr;
- int pthread_cond_init(phtread_cond_t *cond, const pthread_conattr_t *attr);
Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
| #include <time.h=""> #include <pthread.h> #include <stdio.h> static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; static int avail = 0; static void * threadFunc( void *arg) { int cnt = atoi (( char *) arg); int s, j; for (j = 0; j < cnt; j++) { sleep(1); /* Code to produce a unit omitted */ s = pthread_mutex_lock(&mtx); if (s != 0) printf ( "pthread_mutex_lock" ); avail++; /* Let consumer know another unit is available */ s = pthread_mutex_unlock(&mtx); if (s != 0) printf ( "pthread_mutex_unlock" ); s = pthread_cond_signal(&cond); /* Wake sleeping consumer */ if (s != 0) printf ( "ptread_cond_signal" ); } return NULL; } int main( int argc, char *argv[]) { pthread_t tid; int s, j; int totRequired; /* Total number of units that all threads will produce */ int numConsumed; /* Total units so far consumed */ int done; time_t t; t = time (NULL); /* Create all threads */ totRequired = 0; for (j = 1; j < argc; j++) { totRequired += atoi (argv[j]); s = pthread_create(&tid, NULL, threadFunc, argv[j]); if (s != 0) printf ( "pthread_create" ); } /* Loop to consume available units */ numConsumed = 0; done = 0; for (;;) { s = pthread_mutex_lock(&mtx); if (s != 0) printf ( "pthread_mutex_lock" ); while (avail == 0) { /* Wait for something to consume */ s = pthread_cond_wait(&cond, &mtx); if (s != 0) printf ( "pthread_cond_wait" ); } /* At this point, 'mtx' is locked... */ while (avail > 0) { /* Consume all available units */ /* Do something with produced unit */ numConsumed ++; avail--; printf ( "T=%ld: numConsumed=%d\n" , ( long ) ( time (NULL) - t), numConsumed); done = numConsumed >= totRequired; } s = pthread_mutex_unlock(&mtx); if (s != 0) printf ( "pthread_mutex_unlock" ); if (done) break ; /* Perhaps do other work here that does not require mutex lock */ } return 0; } |
Reference
Richard Stones adn Neil Matthew, Begin Linux Programming, 2nd Edition
Michael Kerrisk, The programming interface, 2010
Không có nhận xét nào:
Đăng nhận xét