文章最后更新时间:
一、线程与进程的区别
- 典型的UNIX/Linux进程可以看成只有一个控制线程:一个进程在同一时刻只能做一件事情。有了多个控制线程后,在程序设计时可以把进程设计成在同一时刻做不止一件事,每个线程各自处理独立的任务。
- 进程是程序执行时的一个实例,是担当分配系统资源(CPU时间、内存)的基本单元。在面向线程设计的系统中,进程本身不是基本运行单位,而是线程的容器。程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。
- 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。线程包含了表示进程内执行环境必须的信息,其中包括进程中表示线程的线程ID、一组寄存器值、栈、调度优先级和策略、信号屏蔽字、errno常量以及线程私有数据。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。
- 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程产生影响,而线程只是一个进程中的不同执行路径。进程有自己的堆栈和局部变量,但线程没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
二、使用线程的理由
- 与进程相比,它是一种非常“节俭”的多任务操作方式,内存开销小。
- 线程间通信方便。
三、关于线程的API
1、线程的创建
函数原型:
#include <pthread.h>
//成功执行返回0,否则返回错误编码
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
//Compile and link with -pthread.
参数说明:
2、线程退出
函数原型:
#include <pthread.h>
void pthread_exit(void *retval);
参数说明:
线程调用该函数会直接退出。
3、线程等待
函数原型:
#include <pthread.h>
//成功执行返回0,否则返回错误编码
int pthread_join(pthread_t thread, void **retval);
参数说明:
调用这个函数会一直阻塞该线程,直到指定的线程调用pthread_exit、从启动例程中返回或者被取消。
4、编程实现线程的创建、等待
代码如下:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
// void *(*start_routine) (void *), void *arg);
int cnt=0;
void *func1(void *arg)
{
printf("t1:%ld thread is create\n",(unsigned long)pthread_self());
printf("t1:param is :%d\n",*((int *)arg));
while(1)
{
printf("t1:cnt=%d\n",cnt++);
sleep(1);
}
}
void *func2(void *arg)
{
printf("t2:%ld thread is create\n",(unsigned long)pthread_self());
printf("t2:param is :%d\n",*((int *)arg));
while(1)
{
printf("t2:cnt=%d\n",cnt++);
sleep(1);
}
}
int main()
{
int ret;
int param=100;
pthread_t t1;
pthread_t t2;
int *pret=NULL;
//线程创建
ret=pthread_create(&t1,NULL,func1,(void *)¶m);
pthread_create(&t2,NULL,func2,(void *)¶m);
if(ret==0)
{
printf("main craete t1 success\n");
}
printf("main:%ld thread is create\n",(unsigned long)pthread_self());
while(1);
{ printf("main cnt=%d\n",cnt); }
pthread_join(t1,NULL);
pthread_join(t2,NULL);
return 0;
}
运行结果:
![图片[1]- Linux系统编程—线程- 如烟笔记](https://blog.iosru.com/wp-content/uploads/2024/10/8be3b48c9e1835f23f02892bcd82f4ff.webp)
pthread_self()用于获取线程的ID号。通过运行结果可以看,线程共享着数据段内存空间,同时也能看到线程运行速度不一致和顺序不唯一,谁先运行不知道,目前无法做到同步。
四、互斥锁相关的API
在设计时需要规定所有的线程必须遵守相同的数据访问规则。只有这样,互斥机制才能正常工作。
1、创建和销毁互斥锁
函数原型:
#include <pthread.h>
// 动态初始化,创建互斥锁,若成功返回0,否则返回错误编码
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
//或静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//销毁互斥锁, 若成功返回0,否则返回错误编码
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数说明:
mutex:指定为pthread_mutex_t类型。创建成功会返回为互斥锁的标识符。
attr:要用默认的属性初始化互斥量,只需把attr设置为NULL。
2、加锁和解锁
函数原型:
#include <pthread.h>
//加锁,若成功返回0,否则返回错误编码
int pthread_mutex_lock(pthread_mutex_t *mutex);
//尝试对互斥量进行加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//解锁,若成功返回0,否则返回错误编码
int pthread_mutex_unlock(pthread_mutex_t *mutex);
如果线程不希望被阻塞,它可以使用pthread_mutex_trylock尝试对互斥量进行加锁。如果调用pthread_mutex_trylock时互斥量处于未锁住状态,那么pthread_mutex_trylock将锁住互斥量,不会出现阻塞并返回0,否则就会失败,而返回EBUSY。
3、编程实现加锁和解锁
代码如下:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
int cnt=0;
pthread_mutex_t mutex;
void *func1(void *arg)
{
printf("t1:%ld thread is create\n",(unsigned long)pthread_self());
printf("t1:param is :%d\n",*((int *)arg));
pthread_mutex_lock(&mutex);
while(1)
{
printf("t1:cnt=%d\n",cnt++);
sleep(1);
if(cnt==3)
{
pthread_mutex_unlock(&mutex);
//pthread_exit(NULL);
printf("t1 quit =====================");
exit(0);
}
}
}
void *func2(void *arg)
{
printf("t2:%ld thread is create\n",(unsigned long)pthread_self());
printf("t2:param is :%d\n",*((int *)arg));
pthread_mutex_lock(&mutex);
while(1)
{
printf("t2:cnt=%d\n",cnt);
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main()
{
int ret;
int param=100;
pthread_t t1;
pthread_t t2;
pthread_mutex_init(&mutex,NULL);
ret=pthread_create(&t1,NULL,func1,(void *)¶m);
if(ret==0)
{
printf("main create t1 success\n");
}
ret=pthread_create(&t2,NULL,func2,(void *)¶m);
if(ret==0)
{
printf("main create t2 success\n");
}
while(1)
{
printf("main:cnt=%d\n",cnt);
sleep(1);
}
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
运行结果:
![图片[2]- Linux系统编程—线程- 如烟笔记](https://blog.iosru.com/wp-content/uploads/2024/10/16210368be764e3d232a6bc429457850.webp)
从运行结果,可以看到func1拿到锁之后,只会运行func1的内容,直到func1解锁,func2才能拿到锁,否则等待。
4、线程造成死锁情况
首先需要两把或两把以上的锁,线程1先拿到锁1,线程2先拿到锁2,然后线程1需要再拿到锁2才能运行,线程2需要再拿到锁1才能运行,所以两个线程都拿不到两把锁,而产生互相等待的现象。
代码如下:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
int cnt=0;
pthread_mutex_t mutex;
pthread_mutex_t mutex2;
void *func1(void *arg)
{
pthread_mutex_lock(&mutex);
sleep(1); //休眠
pthread_mutex_lock(&mutex2);
printf("t1:%ld thread is create\n",(unsigned long)pthread_self());
printf("t1:param is :%d\n",*((int *)arg));
pthread_mutex_unlock(&mutex);
pthread_mutex_unlock(&mutex2);
}
void *func2(void *arg)
{
pthread_mutex_lock(&mutex2);
sleep(1); //休眠
pthread_mutex_lock(&mutex);
printf("t2:%ld thread is create\n",(unsigned long)pthread_self());
printf("t2:param is :%d\n",*((int *)arg));
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex);
}
int main()
{
int ret;
int param=100;
pthread_t t1;
pthread_t t2;
pthread_mutex_init(&mutex,NULL);
pthread_mutex_init(&mutex2,NULL);
ret=pthread_create(&t1,NULL,func1,(void *)¶m);
if(ret==0)
{
printf("main create t1 success\n");
}
ret=pthread_create(&t2,NULL,func2,(void *)¶m);
if(ret==0)
{
printf("main create t2 success\n");
}
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex);
pthread_mutex_destroy(&mutex2);
return 0;
}
运行结果:
![图片[3]- Linux系统编程—线程- 如烟笔记](https://blog.iosru.com/wp-content/uploads/2024/10/e4c0c8bbce8c978dd9ff842b825748b3.webp)
由运行结果可知,两个线程产生互相等待的现象,造成死锁。
五、线程条件控制实现线程同步
- 条件变量是线程另一可用的同步机制。条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。
- 条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁定互斥量以后才能计算条件。
1、条件变量的创建及销毁
函数原型:
#include <pthread.h>
//动态初始化,创建成功返回0,否则返回错误编码
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
//或静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//销毁
int pthread_cond_destroy(pthread_cond_t cond);
参数说明:
cond:指定为pthread_cond_t类型。创建成功会返回为条件变量的标识符。
attr:要用默认的属性初始化条件变量,只需把attr设置为NULL。
2、等待
函数原型:
#include <pthread.h>
//成功返回0,否则返回错误编码
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
函数说明: pthread_cond_wait等待条件变为真。如果在给定的时间内条件不能满足,那么会生成一个代表一个出错码的返回变量。 传递给pthread_cond_wait的互斥量对条件进行保护,对条件变量进行加锁。函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁。这两个操作都是原子操作(是指一个操作中的所有动作要么全做,要么全不做)。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。
pthread_cond_wait调用成功返回时,会给该进程进行加锁。
pthread_cond_timedwait函数的工作方式与pthread_cond_wait函数类似,只是多了一个timeout。timeout指定了等待的时间,它是通过timespec结构指定。
3、触发
函数原型:
#include <pthread.h>
//成功返回0,否则返回-1
int pthread_cond_signal(pthread_cond_t cond);
int pthread_cond_broadcast(pthread_cond_t cond);
函数说明: 这两个函数可以用于通知线程条件已经满足。pthread_cond_signal函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast函数将唤醒等待该条件的所有进程。注意,一定要在改变条件状态以后再给线程发信号。
4、编程实现条件变量控制
代码如下:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
int cnt=0;
//静态初始化
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
void *func1(void *arg)
{
printf("t1:%ld thread is create\n",(unsigned long)pthread_self());
printf("t1:param is :%d\n",*((int *)arg));
while(1)
{
pthread_cond_wait(&cond,&mutex);
printf("t1 run ============================\n");
printf("t1:cnt=%d\n",cnt);
cnt=0;
sleep(1);
}
}
void *func2(void *arg)
{
printf("t2:%ld thread is create\n",(unsigned long)pthread_self());
printf("t2:param is :%d\n",*((int *)arg));
while(1)
{
printf("t2:%d\n",cnt);
pthread_mutex_lock(&mutex);
cnt++;
pthread_mutex_unlock(&mutex);
if(cnt==3)
{
pthread_cond_signal(&cond);
}
sleep(1);
}
}
int main()
{
int ret;
int param=100;
pthread_t t1;
pthread_t t2;
//动态初始化
//pthread_mutex_init(&mutex,NULL);
//pthread_cond_init(&cond,NULL);
ret=pthread_create(&t1,NULL,func1,(void *)¶m);
if(ret==0)
{
printf("main create t1 success\n");
}
ret=pthread_create(&t2,NULL,func2,(void *)¶m);
if(ret==0)
{
printf("main create t2 success\n");
}
pthread_join(t1,NULL);
pthread_join(t2,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
return 0;
}
运行结果:
![图片[4]- Linux系统编程—线程- 如烟笔记](https://blog.iosru.com/wp-content/uploads/2024/10/b2acd5805d02d7e41140d05fd83cc496.webp)
个人观点,仅供参考 搬运自公众号:New个程序员
本站收集的资源仅供内部学习研究软件设计思想和原理使用,学习研究后请自觉删除,请勿传播,因未及时删除所造成的任何后果责任自负。
如果用于其他用途,请购买正版支持作者,谢谢!若您认为「RuYan」发布的内容若侵犯到您的权益,请联系站长邮箱: iosruyan@163.com 进行删除处理。
本站资源大多存储在云盘,如发现链接失效,请联系我们,我们会第一时间更新。
暂无评论内容