创建线程
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #define NUM_OF_TASKS 5 // 1. 定义线程函数 void *thread_func(void *arg) { pid_t tid = gettid(); printf("In child thread %d with tid=%d. ", (int)arg, tid); sleep(10); pthread_exit((void *)tid); } int main(int argc, char *argv[]) { pid_t pid=-1, tid=-1; void *p_tid = NULL; pthread_t threads[NUM_OF_TASKS]; // 2. 定义线程对象 int ret; int i; // 3. 设置线程属性:PTHREAD_CREATE_JOINABLE 表示将来主线程等待这个线程的结束,并获取退出时的状态。 pthread_attr_t thread_attr; pthread_attr_init(&thread_attr); pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE); for(i=0;i<NUM_OF_TASKS;i++){ // 4. 创建线程 ret = pthread_create(&threads[i], &thread_attr, thread_func, (void *)i); if (ret){ printf("pthread_create() fail with ret=%d. ", ret); exit(-1); } } // 5. 销毁线程属性 pthread_attr_destroy(&thread_attr); for(i=0;i<NUM_OF_TASKS;i++){ // 6. 等待线程结束,并获取线程的返回值 pthread_join(threads[i],(void**)&p_tid); printf("Child thread %d with tid=%d has exited. ", i, (pid_t)p_tid); } tid = gettid(); pid = getpid(); printf("tid=%d, pid=%d. ", tid, pid); // 7. 主线程结束 pthread_exit(NULL); }
编译、执行
root@ubuntu:/home/cindy/test/thread# gcc thread.c -lpthread ^ root@ubuntu:/home/cindy/test/thread# ./a.out In child thread 1 with tid=271762. In child thread 3 with tid=271764. In child thread 0 with tid=271761. In child thread 2 with tid=271763. In child thread 4 with tid=271765. Child thread 0 with tid=271761 has exited. Child thread 1 with tid=271762 has exited. Child thread 2 with tid=271763 has exited. Child thread 3 with tid=271764 has exited. Child thread 4 with tid=271765 has exited. tid=271760, pid=271760. root@ubuntu:/home/cindy/test/thread#
栈大小
//-a: all current limits are reported cindy@ubuntu:~$ ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 7598 max locked memory (kbytes, -l) 65536 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 7598 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited cindy@ubuntu:~$ // -s: the maximum stack size cindy@ubuntu:~$ ulimit -s 8192 cindy@ubuntu:~$ ulimit -s 4096 cindy@ubuntu:~$ ulimit -s 4096 cindy@ubuntu:~$ // 通过pthread_attr_t,修改线程栈的大小 int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
线程私有数据
// 创建线程私有数据,创建一个 key,伴随着一个析构函数;key 一旦被创建,所有线程都可以访问它。 int pthread_key_create(pthread_key_t *key, void (*destructor)(void*))
// 设置 key 对应的 value,各线程可根据自己的需要往 key 中填入不同的值,相当于同名而不同值的全局变量。 int pthread_setspecific(pthread_key_t key, const void *value)
// 获取 key 对应的 value void *pthread_getspecific(pthread_key_t key)
// 线程退出时会调用析构函数释放 value
互斥锁Mutex
这里构建了一个“转账”的场景:
有两个员工 Tom 和 Jerry,饭卡里面各自有 100 元,并行启动 3 个线程,都是 Jerry 转 10 元给 Tom,主线程不断打印 Tom 和 Jerry 的资金之和。
在没有锁的保护下,在 Tom 的账户里面加上 10 元,在 Jerry 的账户里面减去 10 元,这不是一个原子操作。
可以看到,中间有很多状态不正确,比如两个人的账户之和出现了超过 200 的情况,也就是 Tom 转入了,Jerry 还没转出。
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #define NUM_OF_TASKS 3 int tom_money = 100; int jerry_money = 100; //pthread_mutex_t g_money_lock; void *transfer(void *notused) { pthread_t tid = pthread_self(); printf("Thread %u is transfering money! ", (unsigned int)tid); //pthread_mutex_lock(&g_money_lock); tom_money += 10; sleep(rand()%10); jerry_money -= 10; //pthread_mutex_unlock(&g_money_lock); printf("Thread %u finish transfering money! ", (unsigned int)tid); pthread_exit((void *)0); } int main(int argc, char *argv[]) { pthread_t threads[NUM_OF_TASKS]; int ret; int i; //pthread_mutex_init(&g_money_lock, NULL); for(i=0;i<NUM_OF_TASKS;i++){ ret = pthread_create(&threads[i], NULL, transfer, NULL); if (ret){ printf("ERROR: pthread_create() fail with %d ", ret); exit(-1); } } for(i=0;i<6;i++){ //pthread_mutex_lock(&g_money_lock); printf("tom_money + jerry_money = %d ", tom_money + jerry_money); //pthread_mutex_unlock(&g_money_lock); sleep(rand()%10); } //pthread_mutex_destroy(&g_money_lock); pthread_exit(NULL); } cindy@ubuntu:~/test/thread$ ./a.out Thread 2791757568 is transfering money! tom_money + jerry_money = 200 Thread 2774972160 is transfering money! Thread 2783364864 is transfering money! tom_money + jerry_money = 230 Thread 2791757568 finish transfering money! Thread 2774972160 finish transfering money! tom_money + jerry_money = 210 Thread 2783364864 finish transfering money! tom_money + jerry_money = 200 tom_money + jerry_money = 200 tom_money + jerry_money = 200 cindy@ubuntu:~/test/thread$ ./a.out tom_money + jerry_money = 200 Thread 3603195648 is transfering money! Thread 3586410240 is transfering money! Thread 3594802944 is transfering money! tom_money + jerry_money = 230 Thread 3594802944 finish transfering money! Thread 3603195648 finish transfering money! tom_money + jerry_money = 210 Thread 3586410240 finish transfering money! tom_money + jerry_money = 200 tom_money + jerry_money = 200 tom_money + jerry_money = 200 cindy@ubuntu:~/test/thread$
取消程序中的注释行,即加上mutex,这个结果就正常了,两个账号之和永远是 200。
使用 Mutex,首先要使用 pthread_mutex_init 函数初始化这个 mutex,初始化后,就可以用它来保护共享变量了。
pthread_mutex_lock() 就是去抢那把锁的函数,如果抢到了,就可以执行下一行程序,对共享变量进行访问;如果没抢到,就被阻塞在那里等待。
如果不想被阻塞,可以使用 pthread_mutex_trylock 去抢那把锁,如果抢到了,就可以执行下一行程序,对共享变量进行访问;如果没抢到,不会被阻塞,而是返回一个错误码。
当共享数据访问结束了,别忘了使用 pthread_mutex_unlock 释放锁,让给其他人使用,最终调用 pthread_mutex_destroy 销毁掉这把锁。
cindy@ubuntu:~/test/thread$ ./a.out Thread 2132875008 is transfering money! Thread 2116089600 is transfering money! Thread 2124482304 is transfering money! tom_money + jerry_money = 200 Thread 2132875008 finish transfering money! Thread 2116089600 finish transfering money! Thread 2124482304 finish transfering money! tom_money + jerry_money = 200 tom_money + jerry_money = 200 tom_money + jerry_money = 200 tom_money + jerry_money = 200 tom_money + jerry_money = 200 cindy@ubuntu:~/test/thread$ ./a.out tom_money + jerry_money = 200 Thread 1005934336 is transfering money! Thread 989148928 is transfering money! Thread 997541632 is transfering money! Thread 1005934336 finish transfering money! Thread 989148928 finish transfering money! Thread 997541632 finish transfering money! tom_money + jerry_money = 200 tom_money + jerry_money = 200 tom_money + jerry_money = 200 tom_money + jerry_money = 200 tom_money + jerry_money = 200 cindy@ubuntu:~/test/thread$ ./a.out Thread 2415654656 is transfering money! tom_money + jerry_money = 200 Thread 2398869248 is transfering money! Thread 2407261952 is transfering money! Thread 2398869248 finish transfering money! Thread 2407261952 finish transfering money! Thread 2415654656 finish transfering money! tom_money + jerry_money = 200 tom_money + jerry_money = 200 tom_money + jerry_money = 200 tom_money + jerry_money = 200 tom_money + jerry_money = 200 cindy@ubuntu:~/test/thread$
条件变量&互斥锁
pthread_cond_wait() 会先 pthread_mutex_unlock(),然后阻塞在等待队列里休眠,直到再次被唤醒,大多数情况下是等待的条件成立而被唤醒。
唤醒后,会先 pthread_mutex_lock(),再访问资源。
这里构建了一个“分配任务给员工”的场景:
总共 10 个任务,3个员工,分四批分配,第一批一个任务三个人抢,第二批两个任务,第三批三个任务,正好每人抢到一个,第四批四个任务,可能有一个员工抢到两个任务。
这样三个员工,四批工作,经典的场景差不多都覆盖到了。
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #define NUM_OF_THREAD 3 #define MAX_TASK_QUEUE 11 char tasklist[MAX_TASK_QUEUE]="0123456789"; int head = 0; int tail = 0; int quit = 0; pthread_mutex_t g_task_lock; pthread_cond_t g_task_cv; void *coder(void *notused) { pthread_t tid = pthread_self(); while(!quit){ pthread_mutex_lock(&g_task_lock); while(tail == head){ printf("No task now! Thread %u is waiting! ", (unsigned int)tid); // block-->unlock-->wait-->lock pthread_cond_wait(&g_task_cv, &g_task_lock); if(quit){ pthread_mutex_unlock(&g_task_lock); printf("Thread %u got off work! ", (unsigned int)tid); pthread_exit((void *)0); } printf("Have task now! Thread %u is grabing the task! ", (unsigned int)tid); } char task = tasklist[head++]; pthread_mutex_unlock(&g_task_lock); printf("Thread %u has task %c now! ", (unsigned int)tid, task); sleep(5); printf("Thread %u finished task %c! ", (unsigned int)tid, task); } printf("Thread %u got off work! ", (unsigned int)tid); pthread_exit((void *)0); } int main(int argc, char *argv[]) { pthread_t threads[NUM_OF_THREAD]; int ret; int i; pthread_mutex_init(&g_task_lock, NULL); pthread_cond_init(&g_task_cv, NULL); for(i=0;i<NUM_OF_THREAD;i++){ ret = pthread_create(&threads[i], NULL, coder, NULL); if (ret){ printf("ERROR: pthread_create() fail with %d ", ret); exit(-1); } } sleep(5); for(i=1;i<=4;i++){ pthread_mutex_lock(&g_task_lock); tail+=i; printf(" Assign %d tasks, notify all coders to grab tasks! ", i); pthread_cond_broadcast(&g_task_cv); pthread_mutex_unlock(&g_task_lock); sleep(20); } pthread_mutex_lock(&g_task_lock); printf(" Notify all coders to get off work! "); quit = 1; pthread_cond_broadcast(&g_task_cv); pthread_mutex_unlock(&g_task_lock); pthread_mutex_destroy(&g_task_lock); pthread_cond_destroy(&g_task_cv); pthread_exit(NULL); } cindy@ubuntu:~/test/thread$ ./a.out No task now! Thread 1709418240 is waiting! No task now! Thread 1692632832 is waiting! No task now! Thread 1701025536 is waiting! Assign 1 tasks, notify all coders to grab tasks! Have task now! Thread 1692632832 is grabing the task! Thread 1692632832 has task 0 now! Have task now! Thread 1701025536 is grabing the task! No task now! Thread 1701025536 is waiting! Have task now! Thread 1709418240 is grabing the task! No task now! Thread 1709418240 is waiting! Thread 1692632832 finished task 0! No task now! Thread 1692632832 is waiting! Assign 2 tasks, notify all coders to grab tasks! Have task now! Thread 1701025536 is grabing the task! Thread 1701025536 has task 1 now! Have task now! Thread 1692632832 is grabing the task! Thread 1692632832 has task 2 now! Have task now! Thread 1709418240 is grabing the task! No task now! Thread 1709418240 is waiting! Thread 1701025536 finished task 1! No task now! Thread 1701025536 is waiting! Thread 1692632832 finished task 2! No task now! Thread 1692632832 is waiting! Assign 3 tasks, notify all coders to grab tasks! Have task now! Thread 1701025536 is grabing the task! Thread 1701025536 has task 3 now! Have task now! Thread 1692632832 is grabing the task! Thread 1692632832 has task 4 now! Have task now! Thread 1709418240 is grabing the task! Thread 1709418240 has task 5 now! Thread 1701025536 finished task 3! No task now! Thread 1701025536 is waiting! Thread 1692632832 finished task 4! No task now! Thread 1692632832 is waiting! Thread 1709418240 finished task 5! No task now! Thread 1709418240 is waiting! Assign 4 tasks, notify all coders to grab tasks! Have task now! Thread 1701025536 is grabing the task! Thread 1701025536 has task 6 now! Have task now! Thread 1692632832 is grabing the task! Thread 1692632832 has task 7 now! Have task now! Thread 1709418240 is grabing the task! Thread 1709418240 has task 8 now! Thread 1701025536 finished task 6! Thread 1701025536 has task 9 now! Thread 1709418240 finished task 8! No task now! Thread 1709418240 is waiting! Thread 1692632832 finished task 7! No task now! Thread 1692632832 is waiting! Thread 1701025536 finished task 9! No task now! Thread 1701025536 is waiting! Notify all coders to get off work! Thread 1709418240 got off work! Thread 1692632832 got off work! Thread 1701025536 got off work! cindy@ubuntu:~/test/thread$