一些名词解释
- 临界资源
- 定义:临界资源是指在体系中一次仅允许一个进程访问的共享资源。这些资源可以是硬件资源(如打印机、磁带机等),也可以是软件资源(如共享数据结构、文件等)。
- 特点:多个进程都可能需要访问临界资源,但在同一时候只能有一个进程对其举行访问,否则可能会导致数据不一致或其他错误。例如,多个进程都要使用打印机打印文件,如果同时访问打印机,就会造成打印混乱。
- 临界区
- 定义:临界区是指进程中访问临界资源的那段代码区域。每个进程中访问临界资源的代码是临界区的一部分。
- 作用:为了包管临界资源的互斥访问,进程在进入临界区之前需要举行一些检查和操作,以确保当前没有其他进程在临界区内访问该资源。当进程访问完临界资源后,需要脱离临界区,以便其他进程可以大概访问。
- 原子性
- 定义:原子性是指一个操作或一组操作要么完全执行,要么完全不执行,不存在部分执行的情况。对于临界区的访问,通常要求具有原子性,即进程进入临界区的操作必须是原子的,要么成功进入,要么阻塞等待,不能出现中央状态。
- 举例:比如银行转账操作,从账户 A 向账户 B 转账 100 元,这个操作包含从账户 A 扣款 100 元和向账户 B 加款 100 元两个子操作,这两个子操作必须作为一个整体执行,要么都成功,要么都失败,以包管数据的一致性和完备性。
- 互斥
- 定义:互斥是指多个进程在访问临界资源时的一种约束条件,即当一个进程正在访问某个临界资源时,其他进程必须等待,直到该进程访问结束并开释该资源后,其他进程才能访问。
- 同步
- 同步是一种和谐多个执行单元(如线程或进程)的操作次序和访问共享资源的机制,其重要目标是确保数据的一致性、制止数据竞争和解决资源争用题目。
STL,智能指针和线程安全
STL中的容器是否是线程安全
在 C++ 的尺度模板库(STL)里,大部分容器并非线程安全的。STL 设计之初,并未将线程安全纳入考量。重要是为了制止在单线程环境中因线程安全机制带来不必要的开销。线程安全机制(像加锁操作)会影响性能,若在所有操作中都默认加入,会让单线程程序变慢。因此,STL 把线程安全的控制权交给了开发者。
- 常见 STL 容器的情况
- 次序容器:例如 std::vector、std::list、std::deque 等,它们不具备线程安全特性。要是多个线程同时对这些容器举行读写操作,就可能出现数据竞争的题目,导致程序出现未定义行为。例如,一个线程正在遍历 std::vector,而另一个线程同时对其举行插入或删除元素的操作,就可能破坏容器的内部结构。
- 关联容器:像 std::map、std::set 以及它们的无序版本 std::unordered_map、std::unordered_set 等,同样不是线程安全的。多个线程同时对这些容器举行修改或访问,可能会引发数据不一致或迭代器失效等题目。
- 容器适配器:如 std::stack、std::queue、std::priority_queue 等,也不包管线程安全。多个线程并发操作这些容器时,可能会产生竞态条件。
智能指针是否是线程安全
智能指针是否线程安全不能一概而论,需要根据具体的智能指针类型以及操作情况来分析:
std::unique_ptr
- 通例操作:std::unique_ptr 用于独占对象的所有权,不允许拷贝,只支持移动语义。从设计上来说,std::unique_ptr 自己并不是为多线程环境设计的,其通例的操作(如对象的创建、移动、析构等)不是线程安全的。因为多个线程同时对同一个 std::unique_ptr 举行操作(例如一个线程尝试移动它,另一个线程尝试访问它所管理的对象)会导致数据竞争和未定义行为。
- 线程安全场景:如果多个线程不共享同一个 std::unique_ptr 实例,而是各自持有独立的 std::unique_ptr 实例,那么在每个线程内部对其举行操作是安全的。
std::shared_ptr
- 引用计数操作:std::shared_ptr 的引用计数操作是线程安全的。std::shared_ptr 内部使用原子操作来管理引用计数,这意味着多个线程可以同时对同一个 std::shared_ptr 举行引用计数的增长或减少操作,而不会出现数据竞争。例如,多个线程可以同时复制同一个 std::shared_ptr 实例,引用计数会正确地更新。
- 管理对象的访问:虽然引用计数操作是线程安全的,但对 std::shared_ptr 所管理的对象的访问不是线程安全的。如果多个线程同时访问和修改 std::shared_ptr 所指向的对象,就需要额外的同步机制(如互斥锁)来包管线程安全。
std::weak_ptr
- std::weak_ptr 是一种不控制所指向对象生命周期的智能指针,它是为了配合 std::shared_ptr 而引入的,用于解决循环引用题目。std::weak_ptr 自己的操作(如创建、赋值、检查是否过期等)是线程安全的,因为它依赖于 std::shared_ptr 的原子引用计数机制。但同样,对 std::weak_ptr 所指向对象的访问也需要额外的同步机制来包管线程安全。
智能指针的线程安全特性因类型而异,并且即使某些操作是线程安全的,对其所管理对象的访问通常也需要额外的同步措施。
可重入VS线程安全
概念
- 线程安全:多个线程并发同一段代码时,不会出现差别的结果。常见对全局变量或者静态变量举行操作,并且没有锁保护的情况下,会出现该题目。
- 重入:同一个函数被差别的执行流调用,当前一个流程还没有执行完,就有其他的执行流再次进入,我们称之为重入。一个函数在重入的情况下,运行结果不会出现任何差别或者任何题目,则该函数被称为可重入函数,否则,是不可重入函数
常见的线程不安全的情况
- 不保护共享变量的函数
- 函数状态随着被调用,状态发生变革的函数
- 返回指向静态变量指针的函数
- 调用线程不安全函数的函数
常见的线程安全的情况
- 每个线程对全局变量或者静态变量只有读取的权限,而没有写入的权限,一样平常来说这些线程是安全的
- 类或者接口对于线程来说都是原子操作
- 多个线程之间的切换不会导致该接口的执行结果存在二义性
常见不可重入的情况
- 调用了malloc/free函数,因为malloc函数是用全局链表来管理堆的
- 调用了尺度I/O库函数,尺度I/O库的很多实现都以不可重入的方式使用全局数据结构
- 可重入函数体内使用了静态的数据结构
常见可重入的情况
- 不使用全局变量或静态变量
- 不使用用malloc或者new开辟出的空间
- 不调用不可重入函数
- 不返回静态或全局数据,所有数据都有函数的调用者提供
- 使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据
可重入与线程安全接洽
- 函数是可重入的,那就是线程安全的
- 函数是不可重入的,那就不能由多个线程使用,有可能引发线程安全题目
- 如果一个函数中有全局变量,那么这个函数既不是线程安全也不是可重入的。
可重入与线程安全区别
- 可重入函数是线程安全函数的一种
- 线程安全不肯定是可重入的,而可重入函数则肯定是线程安全的。
- 如果将对临界资源的访问加上锁,则这个函数是线程安全的,但如果这个重入函数若锁还未开释则会产存亡锁,因此是不可重入的。
常见锁的概念
死锁
死锁是指在一组进程中的各个进程均占有不会开释的资源,但因互相申请被其他进程所站用不会开释的资源而处于的一种永世等待状态。
死锁四个必要条件
- 互斥条件:一个资源每次只能被一个执行流使用
- 哀求与保持条件:一个执行流因哀求资源而阻塞时,对已得到的资源保持不放
- 不剥夺条件:一个执行流已得到的资源,在末使用完之前,不能强行剥夺
- 循环等待条件:若干执行流之间形成一种头尾相接的循环等待资源的关系
制止死锁
- 破坏死锁的四个必要条件
- 加锁次序一致
- 制止锁未开释的场景
- 资源一次性分配
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |