电子产业一站式赋能平台

PCB联盟网

搜索
查看: 226|回复: 0
收起左侧

嵌入式Linux:线程同步(读写锁)

[复制链接]

1001

主题

1001

帖子

8809

积分

高级会员

Rank: 5Rank: 5

积分
8809
发表于 2025-2-16 08:02:00 | 显示全部楼层 |阅读模式

tg3x2f4oox464014949133.gif

tg3x2f4oox464014949133.gif
4 ^/ F+ |1 u6 q; c4 X/ ]$ x: l* N
点击上方蓝色字体,关注我们' q; s- q" f' ^5 _' n( K. P

2 v' ^' g) [- j& [2 g" B读写锁相比互斥锁(mutex)或自旋锁(spinlock)具有更高的并行性,因为它有三种状态:读加锁状态、写加锁状态和不加锁状态。
" D# j& _; n% @8 J  Y, z+ R3 P
- q' R% N* @+ g5 a6 I% w读写锁的规则和状态:$ R5 U0 `% g- _
  • 写模式加锁状态:当一个线程获取写锁时,其他所有试图获取该锁的线程(无论是读锁还是写锁)都会被阻塞,直到写锁被释放。
  • 读模式加锁状态:当线程获取读锁时,其他试图获取读锁的线程可以并发成功获取锁,但任何试图获取写锁的线程会被阻塞,直到所有读锁被释放。
    ' O* x2 e, p* x+ _3 T

    & Y, [8 n0 c$ J% {. D. M读写锁的使用场景:8 S8 b9 s* k! E
  • 适用于读操作频繁且写操作较少的情况,这样能够允许多线程并发读取,减少锁的竞争,提高系统的效率。
  • 当需要保护一个共享数据结构,同时支持多个线程读,但限制只有一个线程写时,读写锁是比简单的互斥锁更好的选择。  D" V9 b4 S' o) e* @2 Z

    6 i+ Z! S) b# L0 h- ^1
    ! @/ O. M4 T6 |2 f读写锁的初始化. ]  p- |2 m! ?, F
    在使用读写锁之前,必须对其进行初始化。8 w# }) i1 Y9 C( e' i" f

    0 E4 e0 q1 O/ r6 U- K; F: MLinux使用pthread_rwlock_t数据类型来表示读写锁,初始化方式有以下两种:) O: N' W2 N6 t) R2 i

    % F; f5 i! G' t" e9 T

    5 I" j+ E5 j. q3 l+ b: [静态初始化6 p& P+ ~* S  Z  r/ [9 o% }4 b

    ( `/ v0 J0 x8 G) f
  • pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;9 {$ z; I4 O0 V9 k7 e1 U- q# R

    2 A# e- I& m7 x+ k& w% h+ @1 g动态初始化:使用pthread_rwlock_init()函数初始化:
    : }+ K* d; V  J& l8 n" k3 C2 \6 B4 F0 D$ ]5 q! _
  • int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
    ) s4 ^( g- n) W7 h" C- T参数说明5 `1 o2 M3 y4 t6 h4 D/ u
  • rwlock:指向需要初始化的读写锁对象(类型为pthread_rwlock_t)。
  • attr:指向读写锁属性的指针(类型为pthread_rwlockattr_t),可以设置为NULL,表示使用默认属性。
    " g( L2 Q( o9 I8 @4 B5 x% ^# w
    4 D9 G- ?1 j7 U" Y6 |6 p) @
    3 x! z1 [8 p. p% m
    返回值
    , w! H+ i0 q5 k0 g7 f/ p
  • 成功返回0。
  • 失败返回非0错误码,如:       
    2 W8 n4 c' c# \) R2 ^# ^( |
  • EINVAL:表示无效的属性值或锁对象。
  • EBUSY:锁已初始化。
  • ENOMEM:系统内存不足。: T# f0 M8 M7 d' c

    ) V5 ^! t! f/ @2
    # k9 U: D- x* u+ Z  B+ ~1 M- T销毁读写锁
    7 h, N3 J' @( H; b2 L当不再需要使用读写锁时,应该使用pthread_rwlock_destroy()销毁它。函数原型如下:! f& `) x9 J  N+ W5 S( T
    % e4 ~, D! [. Q( d  A$ M7 _! B
  • int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);/ Y) j0 C9 j+ j% h& ~& C
    参数说明
    - X0 m  a$ J& E6 P) U7 I4 l! D8 P
  • rwlock:指向需要销毁的读写锁对象。, c3 K2 E" n, Z& y- V8 ]. [; D

    # U8 F$ t. @7 V4 h% J
    + @+ O! F% }* K, ^( l. @
    返回值
    ( D% k* j( O8 c& r9 ]. q6 h
  • 成功返回0。
  • 失败返回非0错误码,如:EBUSY:锁被其他线程持有。) P. p/ S2 Q( U- G, f; ^

    ' i5 ?* ~2 t3 E& H  R, V3! P' q9 \$ T8 p
    读写锁加锁与解锁
    " L4 J: T3 R/ G- e0 D以读模式加锁,该函数会阻塞调用线程,直到能够成功获取读锁。
    , k) ]* V; o( E* T, q/ {+ m) w+ u' J
    - ?8 V+ C' G" I. r如果已经有其他线程持有写锁,当前线程将会等待。# k( z9 w( {. c0 `

    ) [) u, y9 G1 e3 n% |2 s
  • int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
    ) b+ n" f! `7 O" P8 o

    . U# Y$ K5 K: N* z: }. G$ M参数说明
    1 U  S' J* b# O6 {) Q4 A
  • rwlock:指向需要加锁的读写锁对象。4 m& x( _0 q) U% C3 V

    6 E, ~9 [! a9 g4 ?
    3 G# s0 V5 O: M: {
    返回值
    8 b$ E6 N1 ^2 L
  • 成功返回0。
  • 失败返回非0错误码,如:        1 l( i% l4 N% t! L0 o, K9 P
  • EINVAL:无效的锁。
  • EDEADLK:检测到死锁。
  • EAGAIN:系统无法分配更多的读锁。- b+ [$ p" e1 [9 v; ]! O2 c
    / }7 s5 s1 K- v& _4 o9 s4 ]6 b
    以写模式加锁,该函数会阻塞调用线程,直到能够成功获取写锁。
      P4 I/ b  x5 M/ E4 A$ ^( r
    5 F( X) [, g: H; y6 |+ y只有当前线程能够获取写锁,其他所有请求锁的线程(无论是读锁还是写锁)都会被阻塞。& |: q# b$ `/ b# I* W

    ' Y- @( f& ^4 \; n8 O6 B
  • int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);4 L0 i, ^/ l' X3 f, B7 ]
    : @( q$ r: u/ O; y& R
    参数说明
      a9 \: y$ O& n
  • rwlock:指向需要加锁的读写锁对象。' \2 U& H' ~, J" S* [7 }

    3 B3 `. G8 a& r7 D4 d3 W" G
      o+ d& R& k2 Y+ n/ ~/ c0 ]
    返回值
    $ C+ O4 |9 [+ X: H3 x5 V
  • 成功返回0。
  • 失败返回非0错误码,如:       
    ( N1 U6 O: Q8 D- l3 h% Y
  • EINVAL:无效的锁。
  • EDEADLK:检测到死锁。9 i" [, W2 }: d) M8 ?# R2 q$ f8 M
    8 G6 w( j  w9 x0 W% ~. u% u
    尝试获取读锁,该函数尝试获取读锁,不会阻塞。/ a. _# y3 W; K
    " y" p7 r' z7 O9 Y+ z  d1 g
    如果锁被其他线程占用,立即返回失败。; U) w) G# x1 W8 [

    7 m$ P5 C4 h' f, v5 {: u0 t
  • int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
    9 e7 N  p( \- R$ O2 n4 D
    1 R. @, W3 ~( M' G# N* Y' L
    参数说明' G" ~2 z4 }2 K% p3 e$ @
  • rwlock:指向需要加锁的读写锁对象。0 s& n( M6 j! u0 `* Y
    5 V: [3 ^# S9 O  T2 C
    2 M# f! B1 ?1 T) M4 V& l
    返回值' g. l) C* m, c2 q- T; p3 b
  • 成功返回0。
  • 失败返回EBUSY表示锁已被占用,当前无法获取。7 ^* \1 S$ Z% E7 U* }
    % R" q; {! a" K1 M/ }
    尝试获取写锁,该函数尝试获取写锁,不会阻塞。
    + e5 `9 \% B) r7 _/ P0 f# c9 \0 e
    7 Q8 B' z# ]1 r; i; f如果锁被其他线程占用,立即返回失败。& n' ~8 d! B2 l

    # G6 J6 Y# ]) q. P6 X" r, H; x0 o
  • int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);" b3 [( d$ z1 D7 ^: w& b
    : R5 y( F  R; ?9 g0 M; L
    参数说明' g9 E# z+ J3 t2 ?2 L& v
  • rwlock:指向需要加锁的读写锁对象。
    $ L% Q1 m0 r5 q- r* q

    ! W' J$ Q$ ^! k  ]+ m4 I
    / [4 x  [+ F; R
    返回值7 t, N1 F  H: u; Y, ], l2 Q6 C& X5 ?
  • 成功返回0。
  • 失败返回EBUSY表示锁已被占用,当前无法获取。
      _5 q; `( Z. ~& v, @3 e2 i

    - X5 {$ @8 q/ f! q/ {( |$ _, T该函数用于释放当前线程持有的锁,无论是读锁还是写锁。
    6 M: Q6 B* m0 v  Z  _3 u# @
    , p" A. b7 b! r1 T  H9 X
  • int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);# L, F; `1 ~; X" @& m

    7 V3 }) J& T' o/ c! {4 j% _- `" Z' T. g参数说明
    0 J) |  a. U8 ?8 m8 u$ Z
  • rwlock:指向需要解锁的读写锁对象。* j% q8 Z. A4 f: o7 c9 f# L# A* e* \
    1 Z* {& ^' z" H; `& Z! j, m3 S
    , I" R+ x$ m1 [) ]" u
    返回值
    / o4 H* N# A6 s
  • 成功返回0。
  • 失败返回非0错误码,如:       
    9 _8 i0 i$ N5 ^- T9 D
  • EINVAL:无效的锁对象。
  • EPERM:当前线程未持有该锁。
    & n$ I' p$ u; V0 [/ z; j
    5 ~9 w! ?- D2 f, G7 {+ X
    4/ B  \) w; d6 |1 p5 R, j
    读写锁的属性& m  R, j! _' u, t
    读写锁也可以有属性,使用pthread_rwlockattr_t数据类型来表示。% V# R: m% G/ i4 L. W! u  }' a

    7 U/ A8 i8 R9 L: K& ~4 J) D$ m6 Q初始化读写锁属性函数原型:
    % J# j9 e: F9 V8 s$ r) J" A' s9 g0 M" H0 I$ R7 {
  • int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
    ! ~0 Q1 ]9 l4 f

    % Q* @# |$ }( a/ p% r2 u1 {, |参数说明
    8 u2 n7 b3 L$ x* E$ [2 Y# g
  • attr:指向需要初始化的读写锁属性对象。
    " {, B' Y: m; P  H3 x+ H: O
    1 N6 D* q' P6 B0 f  ?6 Q. O: }

    0 f1 j5 ~  k9 C: F返回值& I5 r1 ?  t. P) e  I: n6 c
  • 成功返回0。
  • 失败返回非0错误码。( o2 E/ Z: D. \) F3 ?7 i
    " B+ }3 i5 X# J3 x5 X; ^
    读写锁的属性中最常见的是进程共享属性,使用以下函数设置或获取:/ q  x5 H( V' `6 s* u+ q

      @+ [2 Z4 g, d
  • int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);' Y6 P. T$ u# q' ~+ |+ `

    / e) j! s. k% |; u2 U" b$ Q8 ^# }参数说明2 o4 @% Y+ K8 N! O% J: K
  • attr:指向需要设置的读写锁属性对象。
  • pshared:共享属性的值。取值如下:        ) J4 L& p0 S4 v3 f0 j
  • PTHREAD_PROCESS_SHARED:允许多个进程共享该读写锁。
  • PTHREAD_PROCESS_PRIVATE:仅限当前进程的线程共享读写锁(默认值)。- B. O+ y0 o. v3 g2 Q8 ^
    - d) X2 g) T2 t* n0 x
    返回值) c7 U) ~- X% Y  ~9 W8 Y5 W
  • 成功返回0。
  • 失败返回非0错误码。2 t- }, W, I3 F! }( E$ H! I
    & z7 B1 E2 }6 N/ U& A
    以下代码展示了如何在读写锁的保护下,允许多个线程并发读取共享资源,但只有一个线程可以修改它:4 o& g( R# p; d! K$ B

    ) |9 C, p3 |( s9 C' ?! r
  • pthread_rwlock_t rwlock;int shared_data = 0;& ~* F- o' ~( _5 b
    void *reader(void *arg) {    pthread_rwlock_rdlock(&rwlock);  // 获取读锁    printf("Reader: Shared data is %d
    ) I& [2 X, r6 Y( |4 e; `", shared_data);    pthread_rwlock_unlock(&rwlock);  // 解锁    return NULL;}
    , a$ j- d# ^9 t9 D/ q6 u4 bvoid *writer(void *arg) {    pthread_rwlock_wrlock(&rwlock);  // 获取写锁    shared_data += 1;    printf("Writer: Updated shared data to %d
    3 v) ~+ D$ v8 I", shared_data);    pthread_rwlock_unlock(&rwlock);  // 解锁    return NULL;}
    * t0 S' a0 U3 v: G; N7 S: m* Yint main() {    pthread_t r1, r2, w1;
    % u9 m: N: m" b. m    pthread_rwlock_init(&rwlock, NULL);  // 初始化读写锁. \7 d. e& c6 |$ n1 f5 H
        pthread_create(&r1, NULL, reader, NULL);    pthread_create(&w1, NULL, writer, NULL);    pthread_create(&r2, NULL, reader, NULL);# k. d4 ?1 N4 E/ _1 O' f$ l
        pthread_join(r1, NULL);    pthread_join(w1, NULL);    pthread_join(r2, NULL);; K! q5 a  J; e0 J; M$ T& O7 f
        pthread_rwlock_destroy(&rwlock);  // 销毁读写锁    return 0;}  c6 [. _7 Y6 C9 A) J0 E
    Linux中的读写锁适用于提高读密集型应用的并发性。/ n, S4 G+ K3 O

    ' e0 S& _5 @: L; j6 [它能够让多个线程同时读取资源,从而减少锁争用,但也需要合理考虑写饥饿问题。: `# K4 ^3 z8 Y4 p% g8 ]7 `, v

    4 R9 D/ E- h) P3 p; a注意事项如下:
    % d" x0 ]+ W/ _3 P' B8 d1 f
  • 读写锁的潜在问题:如果读操作过于频繁,可能导致写线程长时间无法获取写锁,从而引发写饥饿问题。这通常需要通过其他机制(如优先级)来控制。
  • 使用场景:当读操作远多于写操作时,读写锁能带来性能提升。如果写操作频繁,读写锁可能并不会比互斥锁表现更好。
    8 p2 P: B( r  K
    " W& \6 W  p& s4 ?% }

    3fqf4x3ac4s64014949233.jpg

    3fqf4x3ac4s64014949233.jpg

    2 ~" |; |* U" Z5 ]5 a7 L

    2f2s2amoy0e64014949333.gif

    2f2s2amoy0e64014949333.gif

    : L, e5 v- w6 P点击阅读原文,更精彩~
  • 回复

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表