电子产业一站式赋能平台

PCB联盟网

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

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

[复制链接]

840

主题

840

帖子

6491

积分

高级会员

Rank: 5Rank: 5

积分
6491
发表于 4 天前 | 显示全部楼层 |阅读模式

tg3x2f4oox464014949133.gif

tg3x2f4oox464014949133.gif
! [6 f6 S7 e7 s+ P  ^4 u& J, X
点击上方蓝色字体,关注我们
' @; H; H% P$ z2 p' R0 \% F7 l7 G  w5 ^+ Y9 @) P
读写锁相比互斥锁(mutex)或自旋锁(spinlock)具有更高的并行性,因为它有三种状态:读加锁状态、写加锁状态和不加锁状态。
1 u) Y0 b! b2 `4 s0 H5 h! Y, @; j, t  ^
读写锁的规则和状态:
: [9 ^: h8 l; ]+ \
  • 写模式加锁状态:当一个线程获取写锁时,其他所有试图获取该锁的线程(无论是读锁还是写锁)都会被阻塞,直到写锁被释放。
  • 读模式加锁状态:当线程获取读锁时,其他试图获取读锁的线程可以并发成功获取锁,但任何试图获取写锁的线程会被阻塞,直到所有读锁被释放。
      `* d$ @  f! c% i
    ; ]$ d7 K8 u8 M7 s- I& H
    读写锁的使用场景:
    5 y: H5 Z( Y2 T. V) F$ t( u0 u# D
  • 适用于读操作频繁且写操作较少的情况,这样能够允许多线程并发读取,减少锁的竞争,提高系统的效率。
  • 当需要保护一个共享数据结构,同时支持多个线程读,但限制只有一个线程写时,读写锁是比简单的互斥锁更好的选择。9 o; z* c8 U# {: ]2 L- t9 ^8 j

    / h, }) ]5 ^- d1
    " g7 k2 V+ s7 {" D: `* T读写锁的初始化# t/ w  ]2 Q# U( E) e! ?3 R; F
    在使用读写锁之前,必须对其进行初始化。! p* E) Y+ M1 h+ J6 c8 C

    : @* d; i# R2 h, K+ U/ LLinux使用pthread_rwlock_t数据类型来表示读写锁,初始化方式有以下两种:1 k3 Q# ]5 h! z# {+ H2 j( l/ u
    * b2 A& j) d0 N$ a1 M
    6 V* B& r9 `' o5 G5 v  ]& |& h/ V
    静态初始化% X5 Q0 l6 h1 W1 K  w$ m) ]  e

    5 n: k4 |) C$ i( p: ?: T/ O1 ^
  • pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;# n# P7 W  U% \6 e* X- ^
    . d5 k5 v0 T) f5 H/ j: h1 G  V
    动态初始化:使用pthread_rwlock_init()函数初始化:
    0 ~! [' |, P0 k$ g' _/ P
    . }7 E7 g; ]% U. r4 h- c
  • int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);/ k. S1 `9 o3 t- f- {
    参数说明
    0 L* l  G  }7 B+ S6 B) @, C
  • rwlock:指向需要初始化的读写锁对象(类型为pthread_rwlock_t)。
  • attr:指向读写锁属性的指针(类型为pthread_rwlockattr_t),可以设置为NULL,表示使用默认属性。
    ! @* X3 `2 v* m* s# ^9 N
    0 U, Y2 c5 C8 f) R, J

    2 `4 P8 |8 [* V, l7 `# M: S返回值
    & ]1 l$ V5 W  C, |) Q8 a# W
  • 成功返回0。
  • 失败返回非0错误码,如:       
      T6 K0 I7 I+ |8 ^. L( {# k
  • EINVAL:表示无效的属性值或锁对象。
  • EBUSY:锁已初始化。
  • ENOMEM:系统内存不足。9 R- e- `4 H5 c  ?. n
    - W7 S, p3 a; h2 z6 ^
    2
    / y+ u$ e. e' A+ q' Q1 Q; c销毁读写锁% {; }1 L7 u9 L5 O9 i
    当不再需要使用读写锁时,应该使用pthread_rwlock_destroy()销毁它。函数原型如下:
    3 t. P! {* v4 m2 {% _, r# X8 a
    # M1 z  Q0 s3 G2 o4 J: R. M
  • int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
    % u: o: y" @& L3 m参数说明
    1 r' t' |* K' j1 P9 ^8 d
  • rwlock:指向需要销毁的读写锁对象。) X9 v- }6 o$ G4 X- O1 j

    - `3 e' X" A, m. c- D1 q# L
    ) n% P+ r! U: H5 ]- n- _1 Y
    返回值' H' f/ h9 @4 r
  • 成功返回0。
  • 失败返回非0错误码,如:EBUSY:锁被其他线程持有。
    9 L1 E- T% h* P; _1 Z" y/ T4 x

    - i* \3 [" D& D0 q6 p3! v3 k% I7 G+ k& A/ U5 d/ o
    读写锁加锁与解锁
    7 y! M/ z7 B; m$ b/ c# C$ x以读模式加锁,该函数会阻塞调用线程,直到能够成功获取读锁。7 a8 `4 i7 d+ A% Z! h# f# `

    : k5 ]5 o9 L( o  F如果已经有其他线程持有写锁,当前线程将会等待。  a. Z$ O7 U5 P6 n3 j9 |+ R
    + O- A4 P) t9 ~) ]0 p' J. |
  • int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);  o/ v6 [0 V: f0 y9 P
    + `6 v) J6 T/ S9 A
    参数说明
    & o/ a' h6 H4 \' ]; F
  • rwlock:指向需要加锁的读写锁对象。. y8 |  P' C2 W# X( r& Z5 _; i2 ]

    : J! L; ?% U0 B& B$ ?
    : J; P: G6 k2 c- x/ f
    返回值5 r" r! o* s! D9 h- w6 a& ?7 U3 N
  • 成功返回0。
  • 失败返回非0错误码,如:       
    & w7 J* _( J- W' _) m
  • EINVAL:无效的锁。
  • EDEADLK:检测到死锁。
  • EAGAIN:系统无法分配更多的读锁。9 f. \8 z3 j1 w& P5 {0 J3 V
    2 f6 c3 ]$ U+ [# @* K- r/ w! h
    以写模式加锁,该函数会阻塞调用线程,直到能够成功获取写锁。1 K% l, y) V6 f. G
    6 A5 |  V/ G& I
    只有当前线程能够获取写锁,其他所有请求锁的线程(无论是读锁还是写锁)都会被阻塞。
    ' ~& A$ |: ?: H+ q2 ]" ]& w' u/ z3 c  O' ~  {& {9 F; O
  • int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
    # D' K1 ?. a4 m  _* P1 a

    8 e) S2 [0 U  L1 _( u! x1 i参数说明
    ) A* s! v1 u0 \+ m$ \
  • rwlock:指向需要加锁的读写锁对象。" ?7 [( E. U9 p, ?0 U# r6 n
    + a7 i# \- M7 }! k# j

    " R3 L0 [& g# F# J/ }2 g返回值
    5 J% C, D9 ~" J! I( b& O
  • 成功返回0。
  • 失败返回非0错误码,如:       
    ( I; t6 p5 l9 m, r- t: k5 F
  • EINVAL:无效的锁。
  • EDEADLK:检测到死锁。: P4 z- ^, Y% i
    ; e, Z1 o- E& a+ N% \4 }
    尝试获取读锁,该函数尝试获取读锁,不会阻塞。
    5 I! F& {- z2 l, B7 ^" _* u! D# y3 V$ S8 v$ [! F
    如果锁被其他线程占用,立即返回失败。5 }7 B1 F! Q+ P8 |& V

    + u6 M$ }) m. A' h: H  }
  • int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
    " I% d- Q. H# e% s9 e. M1 |, k
    2 d3 K; e8 c( R; s4 s- q/ @7 Q
    参数说明* X) i$ a$ \4 t, |$ ^. |
  • rwlock:指向需要加锁的读写锁对象。
    + f6 U6 v8 \( N. A$ j
    " X: o9 k8 d* O0 _) y3 e

    2 h* }0 _& D) X; \' X& m  U返回值4 ?2 X8 l# x0 O9 _1 p1 T
  • 成功返回0。
  • 失败返回EBUSY表示锁已被占用,当前无法获取。
    / F. p6 @# f$ W% D3 C1 F* U8 v! Z
    , f8 Z: q& l/ }7 T4 }5 \" _9 s. B# P
    尝试获取写锁,该函数尝试获取写锁,不会阻塞。) r5 q0 M  V" I7 @

    5 J  z5 I  z" b$ x5 ?. |如果锁被其他线程占用,立即返回失败。
    " ]4 f) J2 t8 j& U4 F3 G( _+ u( v4 o3 a; _& Q' `) U6 x
  • int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
    " u0 W/ v0 ^/ L3 g, ^) D
    1 ^- g& L) Z. @5 k' c3 W
    参数说明
    # j4 `. U* _. b# B$ U3 E3 |) m! h
  • rwlock:指向需要加锁的读写锁对象。' C5 J; w6 h% i( `5 M* i2 L( g# O

    / a2 G+ {6 F6 G6 R
    3 W( k% J; o4 {( W  \1 V' H" S2 j
    返回值" `/ e9 Q7 i3 C. Y
  • 成功返回0。
  • 失败返回EBUSY表示锁已被占用,当前无法获取。
    / E+ b8 ?( r9 T% u# a  g" S
    ; `  z5 @+ q/ r
    该函数用于释放当前线程持有的锁,无论是读锁还是写锁。. d5 P- Q+ w# i; [
    - s) F' u2 w. M
  • int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);; D* s) {! J) R  D& h0 K

    ' W! e2 @! u; r3 }5 Y% g参数说明
    ! B- s3 R& X8 |7 U/ L# v# B. ~
  • rwlock:指向需要解锁的读写锁对象。/ }& i4 o8 c! ^  h8 p

    ) R8 t" ^9 Y# F8 z/ d
    0 w4 e- c$ F+ M6 I
    返回值
    6 o1 ?- `8 C. T0 m; i3 Q
  • 成功返回0。
  • 失败返回非0错误码,如:       
    ; y3 p* }  ^) ~- p
  • EINVAL:无效的锁对象。
  • EPERM:当前线程未持有该锁。" c' U7 b: g. i) O
    8 k% f" s  \- g  k
    4
    % \! K8 {5 s* T; R! T读写锁的属性
    2 o' d4 P3 T: g读写锁也可以有属性,使用pthread_rwlockattr_t数据类型来表示。
    . b  K7 e; c0 N0 t! I4 u2 c% c' [
    初始化读写锁属性函数原型:  o' j4 e6 T0 ]- c" f9 K* M

    * C# V  h% p, o. Z, P1 f
  • int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
    1 W" A2 @: e! G, o+ C6 q
    ) q/ A2 n- |, Q3 k+ J, y( M
    参数说明
    8 d* ~) }% H+ }5 Z/ H1 K
  • attr:指向需要初始化的读写锁属性对象。* t, R0 r8 K3 }

    + P" r$ i, @; h* g) K9 l
    ) w. c* X( r7 i* X. w6 J+ F- j, \
    返回值! F! ?8 x# C8 w$ d2 n' p& R
  • 成功返回0。
  • 失败返回非0错误码。
    1 f5 ~, c1 x- o! R. }1 ~

    + I4 y- S8 ?- X3 d4 U读写锁的属性中最常见的是进程共享属性,使用以下函数设置或获取:
    % H6 [- V& O/ w! {% \. @% l
    + u) [. @5 l  A
  • int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);
    4 o2 g/ U/ c' g; d
    ( J, C' g7 |9 h8 a! ~' b/ E+ |- A
    参数说明
    7 q! ^% w& A5 w/ i, h9 ?' B4 u
  • attr:指向需要设置的读写锁属性对象。
  • pshared:共享属性的值。取值如下:        8 Y" {5 f8 b- |3 g
  • PTHREAD_PROCESS_SHARED:允许多个进程共享该读写锁。
  • PTHREAD_PROCESS_PRIVATE:仅限当前进程的线程共享读写锁(默认值)。: K. z) I+ r& i- r$ o

    3 H% d) _( d& w0 |% R" r$ I  o
    返回值2 f" A6 ?2 f. ]* o% l2 G" f5 O
  • 成功返回0。
  • 失败返回非0错误码。  L! X- N' [& G7 v4 q
    3 I1 ~# Q/ P# S7 W
    以下代码展示了如何在读写锁的保护下,允许多个线程并发读取共享资源,但只有一个线程可以修改它:
    ( K6 H6 D( Z; p: `2 q7 \
    3 @  P( C$ {- e  q( l$ j9 B5 E
  • pthread_rwlock_t rwlock;int shared_data = 0;
    ' ?# D" L/ C5 L+ M( o8 h$ wvoid *reader(void *arg) {    pthread_rwlock_rdlock(&rwlock);  // 获取读锁    printf("Reader: Shared data is %d" X8 j0 r& r. u$ `9 r# T
    ", shared_data);    pthread_rwlock_unlock(&rwlock);  // 解锁    return NULL;}5 U9 v, K/ n8 u" p& ~; D9 W
    void *writer(void *arg) {    pthread_rwlock_wrlock(&rwlock);  // 获取写锁    shared_data += 1;    printf("Writer: Updated shared data to %d/ u4 M+ M1 P9 d) Z+ u7 g' S
    ", shared_data);    pthread_rwlock_unlock(&rwlock);  // 解锁    return NULL;}' V* \$ K) k* l* s
    int main() {    pthread_t r1, r2, w1;
    8 [4 `  z6 {7 I$ k' q    pthread_rwlock_init(&rwlock, NULL);  // 初始化读写锁
    1 p! z! X, T6 S1 f  J    pthread_create(&r1, NULL, reader, NULL);    pthread_create(&w1, NULL, writer, NULL);    pthread_create(&r2, NULL, reader, NULL);
    , @% a$ S1 _& Y/ m, y    pthread_join(r1, NULL);    pthread_join(w1, NULL);    pthread_join(r2, NULL);# v6 T! }# D/ @1 G' C& j. e
        pthread_rwlock_destroy(&rwlock);  // 销毁读写锁    return 0;}- x/ G5 G  W2 o2 [: P+ T
    Linux中的读写锁适用于提高读密集型应用的并发性。) {! b0 Y4 `% j6 ^: L

    " R7 F- c3 R" \6 ~+ x" w+ F$ S% ?它能够让多个线程同时读取资源,从而减少锁争用,但也需要合理考虑写饥饿问题。
    7 \& g9 o2 a! F1 w- }8 s
    . w: x6 h8 k( C3 g: a3 }注意事项如下:
    / Q1 |, U0 H$ z) j
  • 读写锁的潜在问题:如果读操作过于频繁,可能导致写线程长时间无法获取写锁,从而引发写饥饿问题。这通常需要通过其他机制(如优先级)来控制。
  • 使用场景:当读操作远多于写操作时,读写锁能带来性能提升。如果写操作频繁,读写锁可能并不会比互斥锁表现更好。+ M. i0 r) v% w! Z& o& q- P
    ! |- ^: b  e2 F, |( B/ r1 n$ i

    3fqf4x3ac4s64014949233.jpg

    3fqf4x3ac4s64014949233.jpg

    4 S2 Z! p: Z' N1 p

    2f2s2amoy0e64014949333.gif

    2f2s2amoy0e64014949333.gif
    1 Y# W/ z; o: b' ^1 m
    点击阅读原文,更精彩~
  • 回复

    使用道具 举报

    发表回复

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

    本版积分规则


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