zoukankan      html  css  js  c++  java
  • 并发与竞争

    并发的途径:

    1.多线程并发访问

    2.抢占式并发访问

    3.中断并发访问

    4.多核(SMP),核间并发访问

    常用防止并发访问手段

    1.原子操作

    原子操作可以保护数据每次操作不被其它操作打断,从而实现数据不被其它操作修改,达到保护数据的目的

    使用结构体

       typedef struct {
        int counter;
       } atomic_t;

    来初始化变量

    定义原子变量atomic_t a;

    定义时初始化atomic_t b = ATOMIC_INIT(0); //定义原子变量 b 并赋初值为 0
    原子变量操作常用函数

    函数 描述
    ATOMIC_INIT(int i) 定义原子变量的时候对其初始化。
    int atomic_read(atomic_t *v) 读取 v 的值,并且返回。
    void atomic_set(atomic_t *v, int i) v 写入 i 值。
    void atomic_add(int i, atomic_t *v) v 加上 i 值。
    void atomic_sub(int i, atomic_t *v) v 减去 i 值。
    void atomic_inc(atomic_t *v) v 1,也就是自增。
    void atomic_dec(atomic_t *v) v 1,也就是自减
    int atomic_dec_return(atomic_t *v) v 1,并且返回 v 的值。
    int atomic_inc_return(atomic_t *v) v 1,并且返回 v 的值。
    int atomic_sub_and_test(int i, atomic_t *v) v i,如果结果为 0 就返回真,否则返回假
    int atomic_dec_and_test(atomic_t *v) v 1,如果结果为 0 就返回真,否则返回假
    int atomic_inc_and_test(atomic_t *v) v 1,如果结果为 0 就返回真,否则返回假
    int atomic_add_negative(int i, atomic_t *v) v i,如果结果为负就返回真,否则返回假

    原子操作缺点:

      原子操作智能对整形变量或者位变量进行保护,在实际环境使用中不太实用

    2.自旋锁

    自旋锁保证多个线程访问公共资源时,每次只有一个线程访问,其余要访问的线程进入自旋等待

    自旋锁结构体

    typedef struct spinlock {
      union {
        struct raw_spinlock rlock;
      
        #ifdef CONFIG_DEBUG_LOCK_ALLOC
        # define LOCK_PADSIZE (offsetof(struct raw_spinlockdep_map))
        struct {
          u8 __padding[LOCK_PADSIZE];
          struct lockdep_map dep_map

          };
        #endif
         };
      spinlock_t;

    定义自旋锁 spinlock_t lock; 

    自旋锁相关API函数

    函数 描述
    DEFINE_SPINLOCK(spinlock_t lock) 定义并初始化一个自选变量。
    int spin_lock_init(spinlock_t *lock) 初始化自旋锁。
    void spin_lock(spinlock_t *lock) 获取指定的自旋锁,也叫做加锁。
    void spin_unlock(spinlock_t *lock) 释放指定的自旋锁。
    int spin_trylock(spinlock_t *lock) 尝试获取指定的自旋锁,如果没有获取到就返回 0
    int spin_is_locked(spinlock_t *lock) 检查指定的自旋锁是否被获取,如果没有被获取就
    返回非 0,否则返回 0

    被自旋锁保护的临界代码区内不能调用任何能够引起睡眠或阻塞的API函数,否则会引起死锁的发生。

    中断也会引起自旋锁的死锁发生,因此在含有中断的公共资源中需要使用以下自旋锁API函数:

    函数 描述
    void spin_lock_irq(spinlock_t *lock) 禁止本地中断,并获取自旋锁。
    void spin_unlock_irq(spinlock_t *lock) 激活本地中断,并释放自旋锁。
    void spin_lock_irqsave(spinlock_t *lock,
    unsigned long flags)
    保存中断状态,禁止本地中断,并获取自旋锁。
    void spin_unlock_irqrestore(spinlock_t
    *lock, unsigned long flags)
    将中断状态恢复到以前的状态,并且激活本地中断,
    释放自旋锁。

     

     考虑到可移植性,设置驱动时最好使用带中断的自旋锁API函数。

    自旋锁的缺点:

    1.多个线程访问同一个带自旋锁的公共资源时,由于自旋锁的存在,只有一个线程可以访问公共资源,而其它的线程会进入原地等待状态,啥也不干。因此自旋锁只适用于运行时间短的驱动,若驱动时间太长会导致系统卡顿。

    2.自旋锁保护的代码区内不能代用任何导致线程进入休眠的API函数,否则会发生死锁

    3.不能递归调用带自旋锁的驱动模块,否则会发生自己把自己锁死的死锁

    3.信号量

    信号量结构体

    struct semaphore {
      raw_spinlock_t lock;
      unsigned int count;
      struct list_head wait_list;
    };

    信号量相关的API函数

    函数 描述
    DEFINE_SEAMPHORE(name) 定义一个信号量,并且设置信号量的值为 1
    void sema_init(struct semaphore *sem, int val) 初始化信号量 sem,设置信号量值为 val
    void down(struct semaphore *sem) 获取信号量,因为会导致休眠,因此不能在中
    断中使用。
    int down_trylock(struct semaphore *sem); 尝试获取信号量,如果能获取到信号量就获
    取,并且返回 0。如果不能就返回非 0,并且
    不会进入休眠。
    int down_interruptible(struct semaphore *sem) 获取信号量,和 down 类似,只是使用 down
    入休眠状态的线程不能被信号打断。而使用此
    函数进入休眠以后是可以被信号打断的。
    void up(struct semaphore *sem) 释放信号量

    信号量的特点:

    1.在多线程访问同一个带信号量的驱动时,信号量可以使等待驱动的线程进入休眠,因此信号量可以适用于运行时间长的驱动。

    2.信号量不能用于中断中。因为信号量会引起休眠,中断不是休眠。

    3.若公共驱动运行时间比较短,使用信号量的效果就没有自旋锁好了。因为频繁的休眠和唤醒所用的时间会远大与信号量节省的时间。

     

    4.互斥体

    互斥体结构体

    struct mutex {
      /* 1: unlocked, 0: locked, negative: locked, possible waiters */
      atomic_t count;
      spinlock_t wait_lock;

    };

    互斥体相关API函数:

    函数 描述
    DEFINE_MUTEX(name) 定义并初始化一个 mutex 变量。
    void mutex_init(mutex *lock) 初始化 mutex
    void mutex_lock(struct mutex *lock) 获取 mutex,也就是给 mutex 上锁。如果获
    取不到就进休眠。
    void mutex_unlock(struct mutex *lock) 释放 mutex,也就给 mutex 解锁。
    int mutex_trylock(struct mutex *lock) 尝试获取 mutex,如果成功就返回 1,如果失
    败就返回 0
    int mutex_is_locked(struct mutex *lock) 判断 mutex 是否被获取,如果是的话就返回
    1,否则返回 0
    int mutex_lock_interruptible(struct mutex *lock) 使用此函数获取信号量失败进入休眠以后可
    以被信号打断。

    互斥体的特点:

    1.互斥体可导致休眠。因此不能在中断中使用互斥体,中断中智能使用自旋锁。

    2.和信号量一样,在互斥体保护的临界区内可以调用引起阻塞的API函数

    3.因为一次只用一个线程可以持有互斥体,因此互斥体智能由互斥体的持有者释放,并且互斥体的持有者不能递归调用互斥体上锁的共享资源。

     

     

  • 相关阅读:
    (一)Kafka0.8.2官方文档中文版系列入门指南
    Hbase TTL(Time To Live)详解
    java源码学习详解Object类
    设计模式详细解读简单工厂方法模式
    (二)Kafka0.8.2官方文档中文版系列API
    Scala对象相等性判断
    scala中跳出循环的3种方法
    wpf 中借助 Grid 实现随着 Form 大小变化而按比例自动改变宽度或高度。
    static and cache
    约定编程之 Dictionary 的 String 类型的 Key
  • 原文地址:https://www.cnblogs.com/qingyunboke/p/12642049.html
Copyright © 2011-2022 走看看