#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); // Compile and link with -pthread.描述:
:
参数 | 含义 |
---|---|
attr | attr参数指向pthread_attr_t结构,其内容在线程创建时用于确定新线程的属性;使用pthread_attr_init()和相关函数初始化该结构。如果attr为空,则使用默认属性创建线程。 |
thread | 在返回之前,成功调用pthread_create()将新线程的ID存储在thread指向的缓冲区中;此标识符用于在后续调用其他pthreads函数时引用线程。 |
start_routine | 线程入口函数 |
arg | 线程入口函数的参数 |
错误号 | 含义 |
---|---|
EAGAIN | 资源不足,无法创建另一个线程。 |
AGAIN A | 遇到系统对线程数量施加的限制。可能触发此错误的限制有很多:已达到RLIMIT_NPROC软资源限制【通过setrlimit()设置】,该限制限制了真实用户ID的进程和线程数;已达到内核对进程和线程数的系统范围限制,即/proc/sys/kernel/threads max【请参阅proc()】;或者达到最大pid数/proc/sys/kernel/pid_max【见proc()】。 |
EINVAL | 属性中的设置无效。 |
EPERM | 没有设置attr中指定的调度策略和参数的权限。 |
其他:
#include <pthread.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <ctype.h> #define handle_error_en(en, msg) \ do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0) #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) struct thread_info { /* Used as argument to thread_start() */ pthread_t thread_id; /* ID returned by pthread_create() */ int thread_num; /* Application-defined thread # */ char *argv_string; /* From command-line argument */ }; /* Thread start function: display address near top of our stack, and return upper-cased copy of argv_string */ static void * thread_start(void *arg) { struct thread_info *tinfo = arg; char *uargv, *p; // 堆代码 duidaima.com printf("Thread %d: top of stack near %p; argv_string=%s\n", tinfo->thread_num, &p, tinfo->argv_string); uargv = strdup(tinfo->argv_string); if (uargv == NULL) handle_error("strdup"); for (p = uargv; *p != '\0'; p++) *p = toupper(*p); return uargv; } int main(int argc, char *argv[]) { int s, tnum, opt, num_threads; struct thread_info *tinfo; pthread_attr_t attr; int stack_size; void *res; /* The "-s" option specifies a stack size for our threads */ stack_size = -1; while ((opt = getopt(argc, argv, "s:")) != -1) { switch (opt) { case 's': stack_size = strtoul(optarg, NULL, 0); break; default: fprintf(stderr, "Usage: %s [-s stack-size] arg...\n", argv[0]); exit(EXIT_FAILURE); } } num_threads = argc - optind; /* Initialize thread creation attributes */ s = pthread_attr_init(&attr); if (s != 0) handle_error_en(s, "pthread_attr_init"); if (stack_size > 0) { s = pthread_attr_setstacksize(&attr, stack_size); if (s != 0) handle_error_en(s, "pthread_attr_setstacksize"); } /* Allocate memory for pthread_create() arguments */ tinfo = calloc(num_threads, sizeof(struct thread_info)); if (tinfo == NULL) handle_error("calloc"); /* Create one thread for each command-line argument */ for (tnum = 0; tnum < num_threads; tnum++) { tinfo[tnum].thread_num = tnum + 1; tinfo[tnum].argv_string = argv[optind + tnum]; /* The pthread_create() call stores the thread ID into corresponding element of tinfo[] */ s = pthread_create(&tinfo[tnum].thread_id, &attr, &thread_start, &tinfo[tnum]); if (s != 0) handle_error_en(s, "pthread_create"); } /* Destroy the thread attributes object, since it is no longer needed */ s = pthread_attr_destroy(&attr); if (s != 0) handle_error_en(s, "pthread_attr_destroy"); /* Now join with each thread, and display its returned value */ for (tnum = 0; tnum < num_threads; tnum++) { s = pthread_join(tinfo[tnum].thread_id, &res); if (s != 0) handle_error_en(s, "pthread_join"); printf("Joined with thread %d; returned value was %s\n", tinfo[tnum].thread_num, (char *) res); free(res); /* Free memory allocated by thread */ } free(tinfo); exit(EXIT_SUCCESS); }1.2、线程的终止
#include <pthread.h> void pthread_exit(void *retval); // Compile and link with -pthread.描述:
#include <pthread.h> int pthread_cancel(pthread_t thread); // Compile and link with -pthread.描述:
#include <pthread.h> int pthread_join(pthread_t thread, void **retval); // Compile and link with -pthread.描述:
pthread_join()函数等待线程指定的线程终止。如果该线程已经终止,则pthread_join()立即返回。thread指定的线程必须是可连接的。如果retval不为空,则pthread_join()将目标线程的退出状态(即,目标线程提供给pthrea_exit()的值)复制到retval所指向的位置。如果目标线程被取消,则PTHREAD_CANCELED被置于retval中。
错误号 | 含义 |
---|---|
EDEADLK | 检测到死锁(例如,两个线程试图彼此连接);或thread指定调用线程。 |
EINVAL | 线程不是可连接线程。 |
EINVAL | 另一个线程已在等待加入此线程。 |
ESRCH | 找不到ID为线程的线程。 |
#include <pthread.h> int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr); // Compile and link with -pthread.描述:
pthread_attr_setaffinity_np(), pthread_attr_setdetachstate(), pthread_attr_setguardsize(), pthread_attr_setinheritsched(), pthread_attr_setschedparam(), pthread_attr_setschedpolicy(), pthread_attr_setscope(), pthread_attr_setstack(), pthread_attr_setstackaddr(), pthread_attr_setstacksize(), pthread_create(), pthread_getattr_np(), pthreads()对已初始化的线程属性对象调用pthread_attr_init()会导致未定义的行为。当不再需要线程属性对象时,应使用pthread_attr_destroy()函数将其销毁。 销毁线程属性对象对使用该对象创建的线程没有影响。线程属性对象被销毁后,可以使用pthread_attr_init()对其重新初始化。任何其他使用已销毁线程属性对象的方法都会产生未定义的结果。
#define _GNU_SOURCE /* To get pthread_getattr_np() declaration */ #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #define handle_error_en(en, msg) \ do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0) static void display_pthread_attr(pthread_attr_t *attr, char *prefix) { int s, i; size_t v; void *stkaddr; struct sched_param sp; s = pthread_attr_getdetachstate(attr, &i); if (s != 0) handle_error_en(s, "pthread_attr_getdetachstate"); printf("%sDetach state = %s\n", prefix, (i == PTHREAD_CREATE_DETACHED) ? "PTHREAD_CREATE_DETACHED" : (i == PTHREAD_CREATE_JOINABLE) ? "PTHREAD_CREATE_JOINABLE" : "???"); s = pthread_attr_getscope(attr, &i); if (s != 0) handle_error_en(s, "pthread_attr_getscope"); printf("%sScope = %s\n", prefix, (i == PTHREAD_SCOPE_SYSTEM) ? "PTHREAD_SCOPE_SYSTEM" : (i == PTHREAD_SCOPE_PROCESS) ? "PTHREAD_SCOPE_PROCESS" : "???"); s = pthread_attr_getinheritsched(attr, &i); if (s != 0) handle_error_en(s, "pthread_attr_getinheritsched"); printf("%sInherit scheduler = %s\n", prefix, (i == PTHREAD_INHERIT_SCHED) ? "PTHREAD_INHERIT_SCHED" : (i == PTHREAD_EXPLICIT_SCHED) ? "PTHREAD_EXPLICIT_SCHED" : "???"); s = pthread_attr_getschedpolicy(attr, &i); if (s != 0) handle_error_en(s, "pthread_attr_getschedpolicy"); printf("%sScheduling policy = %s\n", prefix, (i == SCHED_OTHER) ? "SCHED_OTHER" : (i == SCHED_FIFO) ? "SCHED_FIFO" : (i == SCHED_RR) ? "SCHED_RR" : "???"); s = pthread_attr_getschedparam(attr, &sp); if (s != 0) handle_error_en(s, "pthread_attr_getschedparam"); printf("%sScheduling priority = %d\n", prefix, sp.sched_priority); s = pthread_attr_getguardsize(attr, &v); if (s != 0) handle_error_en(s, "pthread_attr_getguardsize"); printf("%sGuard size = %d bytes\n", prefix, v); s = pthread_attr_getstack(attr, &stkaddr, &v); if (s != 0) handle_error_en(s, "pthread_attr_getstack"); printf("%sStack address = %p\n", prefix, stkaddr); printf("%sStack size = 0x%zx bytes\n", prefix, v); } static void * thread_start(void *arg) { int s; pthread_attr_t gattr; /* pthread_getattr_np() is a non-standard GNU extension that retrieves the attributes of the thread specified in its first argument */ s = pthread_getattr_np(pthread_self(), &gattr); if (s != 0) handle_error_en(s, "pthread_getattr_np"); printf("Thread attributes:\n"); display_pthread_attr(&gattr, "\t"); exit(EXIT_SUCCESS); /* Terminate all threads */ } int main(int argc, char *argv[]) { pthread_t thr; pthread_attr_t attr; pthread_attr_t *attrp; /* NULL or &attr */ int s; attrp = NULL; /* If a command-line argument was supplied, use it to set the stack-size attribute and set a few other thread attributes, and set attrp pointing to thread attributes object */ if (argc > 1) { int stack_size; void *sp; attrp = &attr; s = pthread_attr_init(&attr); if (s != 0) handle_error_en(s, "pthread_attr_init"); s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (s != 0) handle_error_en(s, "pthread_attr_setdetachstate"); s = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); if (s != 0) handle_error_en(s, "pthread_attr_setinheritsched"); stack_size = strtoul(argv[1], NULL, 0); s = posix_memalign(&sp, sysconf(_SC_PAGESIZE), stack_size); if (s != 0) handle_error_en(s, "posix_memalign"); printf("posix_memalign() allocated at %p\n", sp); s = pthread_attr_setstack(&attr, sp, stack_size); if (s != 0) handle_error_en(s, "pthread_attr_setstack"); } s = pthread_create(&thr, attrp, &thread_start, NULL); if (s != 0) handle_error_en(s, "pthread_create"); if (attrp != NULL) { s = pthread_attr_destroy(attrp); if (s != 0) handle_error_en(s, "pthread_attr_destroy"); } pause(); /* Terminates when other thread calls exit() */ }二、无原子操作
#include <stdio.h> #include <pthread.h> #include <unistd.h> #define THREAD_SIZE 10 // 10 * 100000 void *func(void *arg) { int *pcount = (int *)arg; int i = 0; while (i++ < 100000) { (*pcount)++; usleep(1); } } int main(int argc, char **argv) { pthread_t threadid[THREAD_SIZE] = { 0 }; int i = 0; int count = 0; for (i = 0; i < THREAD_SIZE; i++) { pthread_create(&threadid[i], NULL, func, &count); } // 1000w for (i = 0; i < 10; i++) { printf("count = %d\n", count); sleep(1); } return 0; }上述代码执行结果理论上是1000000,但是最后结果是994656。也就是无原子操作下的执行结果小于理论值。
Mov [idx], %eax Inc %eax Mov %eax,[idx]也就是c语言是一条语句,但真正执行时是三条命令。在无原子操作时,就可能出现如下情况:
#include <pthread.h> int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);函数描述:互斥锁的初始化。
#include <pthread.h> int pthread_mutex_destroy(pthread_mutex_t *mutex)销毁一个互斥锁即意味着释放它所占用的资源,且要求锁当前处于开放状态。由于在Linux中,互斥锁并不占用任何资源,因此pthread_mutex_destroy()仅仅检查锁状态(锁定状态则返回EBUSY)。
#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex);描述:
互斥类型 | 含义 |
---|---|
PTHREAD_MUTEX_NORMAL | 不提供死锁检测。尝试重新锁定互斥锁会导致死锁。如果线程尝试解锁它尚未锁定的互斥锁或已解锁的互斥体,则会导致未定义的行为。 |
PTHREAD_MUTEX_ERRORCHECK | 提供错误检查。如果线程尝试重新锁定已锁定的互斥锁,则会返回错误。如果线程尝试解锁尚未锁定的互斥体或已解锁的互斥体,则将返回错误。 |
PTHREAD_MUTEX_RECURSIVE | 互斥锁将保留锁定计数的概念。当线程首次成功获取互斥锁时,锁定计数将设置为 1。每次线程重新锁定此互斥锁时,锁定计数都会递增 1。每次线程解锁互斥体时,锁定计数都会减少 1。当锁定计数达到零时,互斥锁将可供其他线程获取。如果线程尝试解锁尚未锁定的互斥体或已解锁的互斥体,则将返回错误。 |
PTHREAD_MUTEX_DEFAULT | 尝试递归锁定互斥会导致未定义的行为。如果互斥体未被调用线程锁定,则尝试解锁该互斥体会导致未定义的行为。如果互斥体未锁定,则尝试解锁互斥体会导致未定义的行为。 |
返回值:
错误代码 | 含义 |
---|---|
EINVAL | 互斥体是使用具有值PTHREAD_PRIO_PROTECT的协议属性创建的,并且调用线程的优先级高于互斥体的当前优先级上限。 |
EBUSY | 无法获取互斥体,因为它已被锁定。 |
EINVAL | 互斥体指定的值不引用初始化的互斥体对象。 |
EAGAIN | 无法获取互斥锁,因为已超过互斥锁的最大递归锁数。 |
EDEADLK | 当前线程已拥有互斥体。 |
EPERM | 当前线程不拥有互斥体。 |
这些函数不会返回错误代码EINTR。
#include <pthread.h> int pthread_mutex_unlock(pthread_mutex_t *mutex);描述:
#include <stdio.h> #include <pthread.h> #include <unistd.h> #define THREAD_SIZE 10 #define ADD_MUTEX_LOCK 1 #if ADD_MUTEX_LOCK pthread_mutex_t mutex; #endif // 10 * 100000 void *func(void *arg) { int *pcount = (int *)arg; int i = 0; while (i++ < 100000) { #if 0 (*pcount)++; #elif ADD_MUTEX_LOCK pthread_mutex_lock(&mutex); (*pcount)++; pthread_mutex_unlock(&mutex); #endif usleep(1); } } int main(int argc, char **argv) { pthread_t threadid[THREAD_SIZE] = { 0 }; #if ADD_MUTEX_LOCK pthread_mutex_init(&mutex, NULL); #endif int i = 0; int count = 0; for (i = 0; i < THREAD_SIZE; i++) { pthread_create(&threadid[i], NULL, func, &count); } // 堆代码 duidaima.com // 1000w for (i = 0; i < 50; i++) { printf("count = %d\n", count); sleep(1); } return 0; }上述代码执行结果是1000000。也就是互斥锁下的执行结果等于理论值。
#include <pthread.h> // 1. 销毁自旋锁 int pthread_spin_destroy(pthread_spinlock_t *lock); // 2. 初始化自旋锁 int pthread_spin_init(pthread_spinlock_t *lock, int attr); // 3. 自旋锁上锁(阻塞) int pthread_spin_lock(pthread_spinlock_t *lock); // 4. 自旋锁上锁(非阻塞) int pthread_spin_trylock(pthread_spinlock_t *lock); // 5. 自旋锁解锁 int pthread_spin_unlock(pthread_spinlock_t *lock); 以上函数成功都返回0.示例代码
#include <stdio.h> #include <pthread.h> #include <unistd.h> #define THREAD_SIZE 10 #define ADD_MUTEX_LOCK 0 #define ADD_SPIN_LOCK 1 #if ADD_MUTEX_LOCK pthread_mutex_t mutex; #endif #if ADD_SPIN_LOCK pthread_spinlock_t spinlock; #endif // 10 * 100000 void *func(void *arg) { int *pcount = (int *)arg; int i = 0; while (i++ < 100000) { #if 0 (*pcount)++; #elif ADD_MUTEX_LOCK pthread_mutex_lock(&mutex); (*pcount)++; pthread_mutex_unlock(&mutex); #elif ADD_SPIN_LOCK pthread_spin_lock(&spinlock); (*pcount)++; pthread_spin_unlock(&spinlock); #endif usleep(1); } } int main(int argc, char **argv) { pthread_t threadid[THREAD_SIZE] = { 0 }; #if ADD_MUTEX_LOCK pthread_mutex_init(&mutex, NULL); #elif ADD_SPIN_LOCK pthread_spin_init(&spinlock, PTHREAD_PROCESS_SHARED); #endif int i = 0; int count = 0; for (i = 0; i < THREAD_SIZE; i++) { pthread_create(&threadid[i], NULL, func, &count); } // 1000w for (i = 0; i < 50; i++) { printf("count = %d\n", count); sleep(1); } return 0; }上述代码执行结果是1000000。也就是自旋锁下的执行结果等于理论值。
3.spin在发现锁已经被占用时,会一直等着,直到抢到锁。
#include <stdio.h> #include <pthread.h> #include <unistd.h> #define THREAD_SIZE 10 #include <sys/time.h> #define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000) // 堆代码 duidaima.com // 原子操作 int inc(int *value,int add) { int old; __asm__ volatile( "lock; xaddl %2, %1;" : "=a" (old) : "m" (*value),"a"(add) : "cc","memory" ); return old; } // 10 * 1000000 void *func(void *arg) { int *pcount = (int *)arg; int i = 0; while (i++ < 1000000) { inc(pcount, 1); //usleep(1); } } int main(int argc, char **argv) { pthread_t threadid[THREAD_SIZE] = { 0 }; // 统计执行时间 struct timeval tv_start; gettimeofday(&tv_start, NULL); int i = 0; int count = 0; for (i = 0; i < THREAD_SIZE; i++) { pthread_create(&threadid[i], NULL, func, &count); } #if 0 // 1000w for (i = 0; i < 50; i++) { printf("count = %d\n", count); sleep(1); } #else for (i = 0; i < THREAD_SIZE; i++) { pthread_join(threadid[i], NULL); // } #endif struct timeval tv_end; gettimeofday(&tv_end, NULL); int time_used = TIME_SUB_MS(tv_end, tv_start); printf("time_used: %d\n", time_used); return 0; }