网站开发 岗位及职责,宁波外贸公司500强,wordpress memcached redux,江苏定制网站建设费用目录
1 - 分离线程
2 - Linux线程互斥
2.1 - 进程线程间的互斥相关背景概念
2.2 - 互斥量mutex
2.3 - 互斥量的接口
2.4 - 互斥量实现原理探究
3 - 可重入VS线程安全
3.1 - 概念
3.2 - 常见的线程不安全的情况
3.3 - 常见的…目录
1 - 分离线程
2 - Linux线程互斥
2.1 - 进程线程间的互斥相关背景概念
2.2 - 互斥量mutex
2.3 - 互斥量的接口
2.4 - 互斥量实现原理探究
3 - 可重入VS线程安全
3.1 - 概念
3.2 - 常见的线程不安全的情况
3.3 - 常见的线程安全的情况
3.4 - 常见可重入的情况
3.5 - 可重入与线程安全联系
3.6 - 可重入与线程安全区别
4 - 常见锁的概念
4.1 - 死锁
4.2 - 死锁四个必要条件
4.3 - 避免死锁 1 - 分离线程
默认情况下新创建的线程是joinable的线程退出后需要对其进行pthread_join操作否则无法释放资源从而造成系统泄漏。如果不关心线程的返回值join是一种负担这个时候我们可以告诉系统当线程退出时自动释放线程资源。
int pthread_detach(pthread_t thread);可以是线程组内其他线程对目标线程进行分离也可以是线程自己分离。
pthread_detach(pthread_self());joinable和分离是冲突的一个线程不能既是joinable又是分离的。
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include pthread.hvoid* thread_run(void* arg)
{pthread_detach(pthread_self());printf(%s\n, (char*)arg);return NULL;
}int main(void)
{pthread_t tid;if (pthread_create(tid, NULL, thread_run, thread1run...) ! 0 ) {printf(create thread error\n);return 1;}int ret 0;sleep(1);//很重要要让线程先分离再等待if (pthread_join(tid, NULL) 0) {printf(pthread wait success\n);ret 0;}else {printf(pthread wait failed\n);ret 1;}return ret;
}
2 - Linux线程互斥
2.1 - 进程线程间的互斥相关背景概念
临界资源多线程执行流共享的资源就叫做临界资源。临界区每个线程内部访问临界资源的代码就叫做临界区。互斥任何时刻互斥保证有且只有一个执行流进入临界区访问临界资源通常对临界资源起保护作用。原子性不会被任何调度机制打断的操作该操作只有两态要么完成要么未完成。
2.2 - 互斥量mutex
大部分情况线程使用的数据都是局部变量变量的地址空间在线程栈空间内这种情况变量归属单个线程其他线程无法获得这种变量。但有时候很多变量都需要在线程间共享这样的变量称为共享变量可以通过数据的共享完成线程之间的交互。多个线程并发的操作共享变量会带来一些问题。
// 操作共享变量会有问题的售票系统代码
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include pthread.hint ticket 100;void* route(void* arg)
{char* id (char*)arg;while (1) {if (ticket 0) {usleep(1000);printf(%s sells ticket:%d\n, id, ticket);ticket--;}else {break;}}
}int main(void)
{pthread_t t1, t2, t3, t4;pthread_create(t1, NULL, route, thread 1);pthread_create(t2, NULL, route, thread 2);pthread_create(t3, NULL, route, thread 3);pthread_create(t4, NULL, route, thread 4);pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);
} 一次执行结果 thread 4 sells ticket:100 ... thread 4 sells ticket:1 thread 2 sells ticket:0 thread 1 sells ticket:-1 thread 3 sells ticket:-2 为什么可能无法获得争取结果
if语句判断条件为真以后代码可以并发的切换到其他线程。usleep这个模拟漫长业务的过程在这个漫长的业务过程中可能有很多个线程会进入该代码段。--ticket操作本身就不是一个原子操作。 取出 ticket--部分的汇编代码 objdump -d a.out test.objdump 152 40064b: 8b 05 e3 04 20 00 mov 0x2004e3(%rip),%eax # 600b34 ticket 153 400651: 83 e8 01 sub $0x1,%eax 154 400654: 89 05 da 04 20 00 mov %eax,0x2004da(%rip) # 600b34 ticket --操作并不是原子操作而是对应三条汇编指令
load将共享变量ticket从内存加载到寄存器中。update: 更新寄存器里面的值执行-1操作。store将新值从寄存器写回共享变量 ticket 的内存地址。
要解决以上问题需要做到三点
代码必须要有互斥行为当代码进入临界区执行时不允许其他线程进入该临界区。如果多个线程同时要求执行临界区的代码并且临界区没有线程在执行那么只能允许一个线程进入该临界区。如果线程不在临界区中执行那么该线程不能阻止其他线程进入临界区。要做到这三点本质上就是需要一把锁。Linux上提供的这把锁叫互斥量。 2.3 - 互斥量的接口
初始化互斥量
初始化互斥量有两种方法
方法1静态分配
pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER方法2动态分配
int pthread_mutex_init(pthread_mutex_t* restrict mutex, constpthread_mutexattr_t* restrict attr);参数
mutex要初始化的互斥量
attrNULL
销毁互斥量
销毁互斥量需要注意
使用PTHREAD_ MUTEX_ INITIALIZER初始化的互斥量不需要销毁。不要销毁一个已经加锁的互斥量。已经销毁的互斥量要确保后面不会有线程再尝试加锁。
int pthread_mutex_destroy(pthread_mutex_t* mutex);互斥量加锁和解锁
int pthread_mutex_lock(pthread_mutex_t* mutex);
int pthread_mutex_unlock(pthread_mutex_t* mutex);返回值:成功返回 0, 失败返回错误号
调用pthread_ lock时可能会遇到以下情况
互斥量处于未锁状态该函数会将互斥量锁定同时返回成功。发起函数调用时其他线程已经锁定互斥量或者存在其他线程同时申请互斥量但没有竞争到互斥量那么pthread_ lock调用会陷入阻塞(执行流被挂起)等待互斥量解锁。
改进上面的售票系统
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include pthread.h
#include sched.hint ticket 100;
pthread_mutex_t mutex;void* route(void* arg)
{char* id (char*)arg;while (1) {pthread_mutex_lock(mutex);if (ticket 0) {usleep(1000);printf(%s sells ticket:%d\n, id, ticket);ticket--;pthread_mutex_unlock(mutex);// sched_yield(); 放弃 CPU}else {pthread_mutex_unlock(mutex);break;}}
}int main(void)
{pthread_t t1, t2, t3, t4;pthread_mutex_init(mutex, NULL);pthread_create(t1, NULL, route, thread 1);pthread_create(t2, NULL, route, thread 2);pthread_create(t3, NULL, route, thread 3);pthread_create(t4, NULL, route, thread 4);pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);pthread_join(t4, NULL);pthread_mutex_destroy(mutex);
}
2.4 - 互斥量实现原理探究
经过上面的例子大家已经意识到单纯的i或者i都不是原子的有可能会有数据一致性问题。为了实现互斥锁操作大多数体系结构都提供了swap或exchange指令该指令的作用是把寄存器和内存单元的数据相交换由于只有一条指令保证了原子性即使是多处理器平台访问内存的总线周期也有先后一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。现在我们把lock和unlock的伪代码改一下。 3 - 可重入VS线程安全
3.1 - 概念
线程安全多个线程并发同一段代码时不会出现不同的结果。常见对全局变量或者静态变量进行操作并且没有锁保护的情况下会出现该问题。重入同一个函数被不同的执行流调用当前一个流程还没有执行完就有其他的执行流再次进入我们称之为重入。一个函数在重入的情况下运行结果不会出现任何不同或者任何问题则该函数被称为可重入函数否则是不可重入函数。
3.2 - 常见的线程不安全的情况
不保护共享变量的函数。函数状态随着被调用状态发生变化的函数。返回指向静态变量指针的函数。调用线程不安全函数的函数。
3.3 - 常见的线程安全的情况
每个线程对全局变量或者静态变量只有读取的权限而没有写入的权限一般来说这些线程是安全的。类或者接口对于线程来说都是原子操作。多个线程之间的切换不会导致该接口的执行结果存在二义性常见不可重入的情况。调用了malloc/free函数因为malloc函数是用全局链表来管理堆的。调用了标准I/O库函数标准I/O库的很多实现都以不可重入的方式使用全局数据结构。可重入函数体内使用了静态的数据结构。
3.4 - 常见可重入的情况
不使用全局变量或静态变量。不使用malloc或者new开辟出的空间。不调用不可重入函数。不返回静态或全局数据所有数据都有函数的调用者提供。使用本地数据或者通过制作全局数据的本地拷贝来保护全局数据。
3.5 - 可重入与线程安全联系
函数是可重入的那就是线程安全的。函数是不可重入的那就不能由多个线程使用有可能引发线程安全问题。如果一个函数中有全局变量那么这个函数既不是线程安全也不是可重入的。
3.6 - 可重入与线程安全区别
可重入函数是线程安全函数的一种。线程安全不一定是可重入的而可重入函数则一定是线程安全的。如果将对临界资源的访问加上锁则这个函数是线程安全的但如果这个重入函数若锁还未释放则会产生死锁因此是不可重入的。
4 - 常见锁的概念
4.1 - 死锁
死锁是指在一组进程中的各个进程均占有不会释放的资源但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。
4.2 - 死锁四个必要条件
互斥条件一个资源每次只能被一个执行流使用。请求与保持条件一个执行流因请求资源而阻塞时对已获得的资源保持不放。不剥夺条件一个执行流已获得的资源在末使用完之前不能强行剥夺。循环等待条件若干执行流之间形成一种头尾相接的循环等待资源的关系。
4.3 - 避免死锁
破坏死锁的四个必要条件加锁顺序一致避免锁未释放的场景资源一次性分配 感谢各位大佬支持
互三啦