【C++11高性能服务器】Thread Safety Analysis,概念,用法详解

静态检查工具:Clang thread safety annotations
参考连接:Thread Safety Analysis¶

总览

线程安全分析的工作原理非常类似于多线程程序的类型系统。除了声明数据类型(例如intfloat等)之外,程序员还可以(选择性地)声明如何在多线程环境中控制对数据的访问。例如,如果foo是由互斥锁mu保护的,那么当一段代码在没有首先锁定mu的情况下对foo进行读写时,分析就会发出警告。类似地,如果有一些特定的例程应该只由GUI线程调用,那么分析将在其他线程调用这些例程时发出警告。

主要宏一览

除NO_THREAD_SAFETY_ANALYSIS需放在定义,其余均放于声明处!

GUARDED_BY(mu) 数据成员收到mu保护,读共享,写排他(即写前锁)
REQUIRES(mu) 调用函数或方法前,需要mu,即函数进入前已持有,退出后仍持有
ACQUIRE(mu) 调用函数或方法时,持有mu,即函数进入时才持有,退出后不释放
RELEASE(mu) 调用函数或方法时,释放mu,即函数进入前已持有,退出前释放
EXCLUDES(mu) 调用函数或方法时,不需mu,即函数进入前不能持有,退出后自然不能释放
NO_THREAD_SAFETY_ANALYSIS 调用函数或方法时,关闭对其线程安全检查
RETURN_CAPABILITY(mu) **声明函数返回对给定能力的引用,**用于注释返回互斥对象的getter方法。
CAPABILITY(<string>) 指定**类的对象(this)**可以作为能力使用,配合无参数ACQUIRERELEASE使用
SCOPE_CAPABILITY 实现RAII-style锁的类的一个属性,其功能在构造函数中获得,在析构函数中释放
TRY_ACQUIRE<bool,mu> 试图获得给定的功能,并返回一个指示成功或失败的布尔值。
ASSET_CAPABILITY(mu) 它断言调用线程已经拥有给定的能力
  • 其余关键组合词(如ACQUIRE与ACQUIRE_SHARED)
    • PT pointer
    • SHARED 支持共享访问
    • GENERIC 支持独占和共享访问

入门实操

通过代码注解(annotations)告诉编译器哪些成员变量和成员函数受mutex保护,若忘记加锁,编译器给出警告。
目的:
他人后续维护代码时,特别容易遗漏线程安全的假设。此工具能将原作者设计意图以代码注解清楚地写出来并让编译器【自动检查】!
注意:

  • GUARDED_BY 表明哪个成员变量被mutex保护
  • clang-Wthead-safety编译代码

代码示例

#include "mutex.h"

class BankAccount {
   
private:
  Mutex mu;
  int   balance GUARDED_BY(mu);

  void depositImpl(int amount) {
   
    balance += amount;       // WARNING! Cannot write balance without locking mu.
  }

  void withdrawImpl(int amount) REQUIRES(mu) {
   
    balance -= amount;       // OK. Caller must have locked mu.
  }

public:
  void withdraw(int amount) {
   
    mu.Lock();
    withdrawImpl(amount);    // OK.  We've locked mu.
  }                          // WARNING!  Failed to unlock mu.

  void transferFrom(BankAccount& b, int amount) {
   
    mu.Lock();
    b.withdrawImpl(amount);  // WARNING!  Calling withdrawImpl() requires locking b.mu.
    depositImpl(amount);     // OK.  depositImpl() has no requirements.
    mu.Unlock();
  }
};

编译方式:-Wthread-safety
clang -c -Wthread-safety example.cpp

代码解析

GUARDED_BY(mu):对于balance读写之前,必须对mu上锁,保证增减操作原子性。
REQUIRES(mu): 表示调用函数前“【假定】保证对mu上锁,此为前提”,故函数内操作balance是线程安全的。
withdraw中报错WARNING! Failed to unlock mu.说明REQUIRES可检测锁的解锁操作;
在24行报错WARNING! Calling withdrawImpl() requires locking b.mu.,说明线程分析可理解不同的锁。

基本概念:功能

线程安全分析提供了一种用保护资源的能力。资源可以是数据成员,也可以是提供对底层资源访问的函数/方法。分析确保调用线程不能访问资源(即调用函数,或读写数据),除非它有能力这样做。
能力与已命名的c++对象相关联,这些对象声明特定的方法来获取和释放能力。对象的名称用于识别功能,最常见的例子是互斥锁。例如,如果mu是一个互斥锁,那么调用mu. lock()会使调用线程获得访问受mu保护的数据的能力。类似地,调用mu.Unlock()会释放该能力。
一个线程的能力可共享、可独占。 比如说多读单写模式,就包含了读能力共享,写能力独占。
程序运行的任意时刻,线程均有用一组特定的能力,可访问给定资源。线程仅能与其他线程互相释放能力或获取能力,而不能拷贝能力,亦或是摧毁。【注解】假设底层实现以适当方式切换,而不知用于获取和释放的具体机制。

参考指南

线程安全分析使用属性来声明线程约束。属性必须附加到命名声明,如类、方法和数据成员。强烈建议用户为各种属性定义宏;示例定义可以在下面的mutex.h中找到。下面的文档假设使用宏。
这些属性只控制线程安全分析做出的假设和它发出的警告。它们不会影响生成的代码或运行时的行为。

属性示例

GUARDED_BY(c) and PT_GUARDED_BY(c) : 数据成员

GUARDED_BY是数据成员上的一个属性,它声明数据成员受到给定能力的保护。对数据的读操作需要共享访问,而写操作需要排他访问。
PT_GUARDED_BY与上述,但用于指针和智能指针。数据成员本身没有约束,但是它所指向的数据受到给定能力的保护。

GUARDED_BY(mu)能力:
能力为mu,为数据成员p1提供mu的保护,即对数据的读操作需要共享访问,而写操作需要排他访问。

Mutex mu;
int p1             GUARDED_BY(mu);
int *p2             PT_GUARDED_BY(mu);
unique_ptr<int> p3  PT_GUARDED_BY(mu);

void test() {
   
  p1 = 0;             // Warning!

  *p2 = 42;           // Warning!
  p2 = new int;       // OK.

  *p3 = 42;           // Warning!
  p3.reset(new int);  // OK.
}

REQUIRES(…), REQUIRES_SHARED(…) :函数或方法

REQUIRES是函数或方法上的一个属性,它声明调用线程必须独占访问给定的能力。可以指定多个功能。功能必须在函数进入前持有,退出时后必须持有。
REQUIRES_SHARED 类似,但仅支持共享访问

REQUIRES(mu1, mu2)能力解释:
能力为mu1,mu2,在函数进入前持有,退出时后必须持有

Mutex mu1, mu2;
int a GUARDED_BY(mu1);
int b GUARDED_BY(mu2);

void foo() REQUIRES(mu1, mu2) {
   
  a = 0;
  b = 0;
}

void test() {
   
  mu1.Lock();
  foo();         // Warning!  Requires mu2.
  mu1.Unlock();
}

ACQUIRE(…), ACQUIRE_SHARED(…), RELEASE(…),RELEASE_SHARED(…),RELEASE_GENERIC(…)

适用于函数或方法

有参数

ACQUIREACQUIRE_SHARED是函数或方法上的属性,声明函数获得能力,但不释放它。该能力进入前不持有,但在退出后持有(SHARED区别同上)。
RELEASE则相反,声明函数释放能力,即能力在进入前持有,退出前释放

  • SHARED:共享
  • GENERIC:支持独占与共享

ACQUIRE(mu)能力解释:
能力为mu,在进入lockAndInit后持有,退出后持有

Mutex mu;
MyClass myObject GUARDED_BY(
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值