1.多线程示例
在 Linux 系统中,创建线程用的函数是 pthread_create;而线程的退出,一般用 pthread_exit 主动退出。
如果使用 exit 函数使进程结束,此时进程中的所有线程都会因进程的结束而结束。 而 pthread_join 函数用于将当前线程挂起,等待线程的结束,它是一个线程阻塞函数,调用它的函数将一直到被等待的线程结束为止,当函数返回时,被等待线程的资源被回收
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//pthread_t pthread_self(void);获取线程ID
//int pthread_equal(pthread_t t1, pthread_t t2);比较两线程ID是否相等
void thread1()
{
int i = 0;
while (i++ < 3)
{
printf("this is pthread1\r\n");
if (i == 2)
{
pthread_exit(0); //退出
sleep(2); //休眠2S
}
}
}
void thread2()
{
int i = 0;
while (i++ < 3)
{
printf("this is pthread2\r\n");
}
pthread_exit(0);
}
int main(int argc, char **argv)
{
pthread_t id1, id2;
int ret;
ret = pthread_create(&id1, NULL, (void *)thread1, NULL); //创建线程,第二个参数设置属性,第四个参数可以传递参数
if (ret != 0)
{
printf("create pthread1 error\r\n");
exit(1);
}
ret = pthread_create(&id2, NULL, (void *)thread2, NULL);
if (ret != 0)
{
printf("create pthread2 error\r\n");
exit(1);
}
pthread_join(id1, NULL); //等待线程结束
pthread_join(id2, NULL);
return 0;
}
结果:编译的时候要带上库,否则提示函数没有定义gcc file_io.c -lpthread
理论上每次结果未必一样。
命令行查看线程,ps -T(ps -aT,单独ps只能看进程),或者去/proc/进程ID/task文件夹去看
this is pthread2
this is pthread2
this is pthread2
this is pthread1
this is pthread1
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define NUM_THREADS 4
void *BusyWork(void *t) /* 线程函数 */
{
int i;
long tid;
double result = 0.0;
tid = (long)t;
printf("Thread %ld starting...\n", tid);
for (i = 0; i < 1000000; i++)
{
result = result + sin(i) * tan(i); /* 进行数学运算 */
}
printf("Thread %ld done. Result = %e\n", tid, result);
pthread_exit((void *)t); /* 带计算结果退出 */
}
int main(int argc, char *argv[])
{
pthread_t thread[NUM_THREADS];
int rc;
long t;
void *status;
for (t = 0; t < NUM_THREADS; t++)
{
printf("Main: creating thread %ld\n", t);
rc = pthread_create(&thread[t], NULL, BusyWork, (void *)t); /* 创建线程 */
if (rc)
{
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
for (t = 0; t < NUM_THREADS; t++)
{
rc = pthread_join(thread[t], &status); /*等待线程终止,并获取返回值,非分离线程释放资源必须调用此函数或者等待进程退出(分离线程退出会自动释放资源)*/
if (rc)
{
printf("ERROR; return code from pthread_join() is %d\n", rc);
exit(-1);
}
printf("Main: completed join with thread %ld having a status of %ld\n", t, (long)status);
}
printf("Main: program completed. Exiting.\n");
pthread_exit(NULL);
}
2.线程属性
pthread_create 函数的第二个参数,就是线程的属性, NULL 表示采用默认的属性。 线程的多项属性都是可以修改的。这些属性主要包括绑定属性、分离属性、堆栈地址、堆栈大小、优先级。其中系统默认的属性为非绑定、非分离、缺省 1M 大小的堆栈,与父进程同样级别的优先级。咱们重点来讲讲如何对这些属性进行设置,往往这些设置有固定的步骤。 通常,首先调用 pthread_attr_init 函数进行初始化,然后调用相应的属性设置函数。如果要设置绑定属性那么使用 pthread_attr_setscope,如果要设置分离属性则使 用 pthread_attr_setdetachstate , 设 置 线 程 优 先 级 则 使 用pthread_attr_getscheparam 和 pthread_attr_setchedparam。完成这些属性的设置后,就可以调用 pthread_create 函数来创建线程了
pthread_t id1, id2;
int ret;
pthread_attr_t attr;
pthread_attr_init(&attr); //初始化
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); //设置绑定属性
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //设置分离属性
ret = pthread_create(&id1, &attr, (void *)thread1, NULL); //传入该参数
if (ret != 0)
{
printf("create pthread1 error\r\n");
exit(1);
}
3.posix互斥锁
互斥锁的操作主要包括以下几个步骤:
1. 互斥锁初始化: pthread_mutex_init
2. 互斥锁上锁: pthread_mutex_lock, pthread_mutex_trylock(非阻塞)
3. 互斥锁解锁: pthread_mutex_unlock
4. 消除互斥锁: pthread_mutex_destroy
其中,互斥锁可以分为快速互斥锁、递归互斥锁和检错互斥锁。这三种锁的区别主要在于其他未占有互斥锁的现场在希望得到互斥锁时,是否需要阻塞等待。快速锁是指调用线程会阻塞直到拥有互斥锁的线程解锁为止。递归互斥锁能够成功返回并且增加调用线程在互斥上加锁的次数(简单说就是同一个线程可以多次去获得锁,信号量就不行),而检错互斥锁则为快速互斥锁的非阻塞版本,它会立刻返回一个错误信息
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
//静态初始化
//pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;默认是快速互斥锁
//pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;递归互斥锁
//pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;检错互斥锁
//动态初始化int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int lock_var;
timer_t end_time;
void thread1(void)
{
int i = 0;
while (time(NULL) < end_time)
{
if (pthread_mutex_lock(&mutex) != 0)
{
perror("pthread_mutex_lock");
}
else
{
printf("pthread1: pthread1 lock the variable\r\n");
for (i = 0; i < 2; i++)
{
sleep(1);
lock_var++;
}
}
if (pthread_mutex_unlock(&mutex) != 0)
{
perror("pthread_mutex_unlock");
}
else
{
printf("pthread1: pthread1 unlock the variable\r\n");
sleep(1);
}
}
}
void thread2(void)
{
int ret;
while (time(NULL) < end_time)
{
ret = pthread_mutex_trylock(&mutex);
if (ret == EBUSY)
{
printf("pthread2:the variable is lock by pthread1\r\n");
}
else
{
if (ret != 0)
{
perror("pthread_mutex_trylock");
exit(1);
}
else
{
printf("pthread2:pthread2 got lock. The variableis %d\n", lock_var);
}
if (pthread_mutex_unlock(&mutex) != 0)
{
perror("pthread_mutex_unlock");
}
else
{
printf("pthread2: pthread2 unlock the variable\r\n");
}
}
sleep(3);
}
}
int main(int argc, char **argv)
{
pthread_t id1, id2;
int ret;
end_time = time(NULL) + 10;
pthread_mutex_init(&mutex, NULL);
ret = pthread_create(&id1, NULL, (void *)thread1, NULL);
if (ret != 0)
{
printf("create pthread1 error\r\n");
exit(1);
}
ret = pthread_create(&id2, NULL, (void *)thread2, NULL);
if (ret != 0)
{
printf("create pthread2 error\r\n");
exit(1);
}
pthread_join(id1, NULL);
pthread_join(id2, NULL);
return 0;
}
结果如下
`pthread2:pthread2 got lock. The variableis 0
pthread2: pthread2 unlock the variable
pthread1: pthread1 lock the variable
pthread1: pthread1 unlock the variable
pthread2:pthread2 got lock. The variableis 2
pthread2: pthread2 unlock the variable
pthread1: pthread1 lock the variable
pthread1: pthread1 unlock the variable
pthread2:pthread2 got lock. The variableis 4
pthread2: pthread2 unlock the variable
pthread1: pthread1 lock the variable
pthread1: pthread1 unlock the variable
pthread2:pthread2 got lock. The variableis 6
pthread2: pthread2 unlock the variable
pthread1: pthread1 lock the variable
pthread1: pthread1 unlock the variable
4.条件变量
主要用于线程间同步,不满足条件时线程休眠,满足条件才执行,但也不是100%同步,signal 与 wait 调用之间有间隙存在
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//静态init
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr)//动态init
int pthread_cond_destroy(pthread_cond_t *cond);//销毁
等待
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
通知
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
5.posix信号量
信号量其实就是一个非负的整数计数器,是操作系统中所用的 PV 原语,它主要应用于进程或线程间的同步与互斥。其工作原理也很简单, PV 原语就是对整数计数器信号量 sem 进行操作,一次 P 操作使 sem 减一,而一次 V 操作使 sem 加一。 当信号量 sem 的值大于等于零时,该线程具有访问公共资源的权限;相反,当信号量 sem 的值小于零时,该线程就阻塞直到信号量 sem 的值大于等于 0 为止
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <semaphore.h>
/* 定义一个信号量 */
sem_t sem;
/* 待观察计数变量 */
int lock_var;
timer_t end_time;
/* 线程一 */
void thread1(void)
{
int i = 0;
while(time(NULL) < end_time)
{
/* P操作,使得sem减一 */
sem_wait(&sem);
for(i =0;i < 2;i++)
{
sleep(1);
lock_var++;
printf("lock_var = %d\n",lock_var);
}
printf("pthread1:lock_var = %d\n",lock_var);
/* V操作,使得sem加一 */
sem_post(&sem);
sleep(1);
}
}
/* 线程二 */
void thread2(void)
{
int ret;
while(time(NULL) < end_time)
{
/* P操作,使得sem减一 */
sem_wait(&sem);
printf("pthread2:pthread2 got lock;lock_var = %d\n",lock_var);
/* V操作,使得sem加一 */
sem_post(&sem);
sleep(3);
}
}
int main()
{
pthread_t id1,id2;
int ret;
end_time = time(NULL) + 10;
/* 初始化信号量为1 */
ret = sem_init(&sem,0,1);
if(ret != 0)
{
printf("sem_init error\n");
exit(1);
}
/* 分别创建线程1、2 */
ret = pthread_create(&id1,NULL,(void *)thread1,NULL);
if(ret != 0)
{
printf("Create pthread1 error\n");
exit(1);
}
ret = pthread_create(&id2,NULL,(void *)thread2,NULL);
if(ret != 0)
{
printf("Create pthread2 error\n");
exit(1);
}
/* 等待线程结束 */
pthread_join(id1,NULL);
pthread_join(id2,NULL);
return 0;
}