精华内容
下载资源
问答
  • 自旋锁和互斥锁区别

    2020-07-18 05:33:26
    线程同步(Thread Synchronization)是并行编程中非常重要的通讯手段,其中最典型的应用就是用Pthreads提供的机制(lock)来对多个线程之间共 享的临界区(Critical Section)进行保护(另一种常用的同步机制是barrier)。
  • 自旋互斥锁 读写锁 递归锁

    千次阅读 2016-04-06 17:13:03
    互斥锁(mutexlock): 最常使用于线程同步的锁;标记用来保证在任一时刻,只能有一个线程访问该对象,同一线程多次加锁操作会造成死锁;临界区和互斥量都可用来实现此锁,通常情况下锁操作失败会将该线程睡眠等待锁...

    互斥锁(mutexlock):

    最常使用于线程同步的锁;标记用来保证在任一时刻,只能有一个线程访问该对象,同一线程多次加锁操作会造成死锁;临界区和互斥量都可用来实现此锁,通常情况下锁操作失败会将该线程睡眠等待锁释放时被唤醒

    自旋锁(spinlock):

    同样用来标记只能有一个线程访问该对象,在同一线程多次加锁操作会造成死锁;使用硬件提供的swap指令或test_and_set指令实现;同互斥锁不同的是在锁操作需要等待的时候并不是睡眠等待唤醒,而是循环检测保持者已经释放了锁,这样做的好处是节省了线程从睡眠状态到唤醒之间内核会产生的消耗,在加锁时间短暂的环境下这点会提高很大效率

    读写锁(rwlock):

    高级别锁,区分读和写,符合条件时允许多个线程访问对象。处于读锁操作时可以允许其他线程和本线程的读锁, 但不允许写锁, 处于写锁时则任何锁操作都会睡眠等待;常见的操作系统会在写锁等待时屏蔽后续的读锁操作以防写锁被无限孤立而等待,在操作系统不支持情况下可以用引用计数加写优先等待来用互斥锁实现。 读写锁适用于大量读少量写的环境,但由于其特殊的逻辑使得其效率相对普通的互斥锁和自旋锁要慢一个数量级;值得注意的一点是按POSIX标准 在线程申请读锁并未释放前本线程申请写锁是成功的,但运行后的逻辑结果是无法预测

    递归锁(recursivelock):

    严格上讲递归锁只是互斥锁的一个特例,同样只能有一个线程访问该对象,但允许同一个线程在未释放其拥有的锁时反复对该锁进行加锁操作; windows下的临界区默认是支持递归锁的,而linux下的互斥量则需要设置参数PTHREAD_MUTEX_RECURSIVE_NP,默认则是不支持

    大读者锁(brlock-Big Reader Lock)

    大读者锁是读写锁的高性能版,读者可以非常快地获得锁,但写者获得锁的开销比较大。大读者锁只存在于2.4内核中,在2.6中已经没有这种锁(提醒读者特别注意)。它们的使用与读写锁的使用类似,只是所有的大读者锁都是事先已经定义好的。这种锁适合于读多写少的情况,它在这种情况下远好于读写锁。

    大读者锁的实现机制是:每一个大读者锁在所有CPU上都有一个本地读者写者锁,一个读者仅需要获得本地CPU的读者锁,而写者必须获得所有CPU上的锁。

    大读者锁的API非常类似于读写锁,只是锁变量为预定义的锁ID。

    大内核锁(BKL--Big Kernel Lock)

    大内核锁本质上也是自旋锁,但是它又不同于自旋锁,自旋锁是不可以递归获得锁的,因为那样会导致死锁。但大内核锁可以递归获得锁。大内核锁用于保护整个内核,而自旋锁用于保护非常特定的某一共享资源。进程保持大内核锁时可以发生调度,具体实现是:在执行schedule时,schedule将检查进程是否拥有大内核锁,如果有,它将被释放,以致于其它的进程能够获得该锁,而当轮到该进程运行时,再让它重新获得大内核锁。注意在保持自旋锁期间是不运行发生调度的。

    需要特别指出,整个内核只有一个大内核锁,其实不难理解,内核只有一个,而大内核锁是保护整个内核的,当然有且只有一个就足够了。

    还需要特别指出的是,大内核锁是历史遗留,内核中用的非常少,一般保持该锁的时间较长,因此不提倡使用它。从2.6.11内核起,大内核锁可以通过配置内核使其变得可抢占(自旋锁是不可抢占的),这时它实质上是一个互斥锁,使用信号量实现。

    RCU(Read-Copy Update)

    RCU(Read-Copy Update),顾名思义就是读-拷贝修改,它是基于其原理命名的。对于被RCU保护的共享数据结构,读者不需要获得任何锁就可以访问它但写者在访问它时首先拷贝一个副本,然后对副本进行修改,最后使用一个回调(callback)机制在适当的时机把指向原来数据的指针重新指向新的被修改的数据。这个时机就是所有引用该数据的CPU都退出对共享数据的操作。

    RCU也是读写锁的高性能版本,但是它比大读者锁具有更好的扩展性和性能。 RCU既允许多个读者同时访问被保护的数据,又允许多个读者和多个写者同时访问被保护的数据(注意:是否可以有多个写者并行访问取决于写者之间使用的同步机制),读者没有任何同步开销,而写者的同步开销则取决于使用的写者间同步机制。但RCU不能替代读写锁,因为如果写比较多时,对读者的性能提高不能弥补写者导致的损失。

    顺序锁(seqlock)

    顺序锁也是对读写锁的一种优化,对于顺序锁,读者绝不会被写者阻塞,也就说,读者可以在写者对被顺序锁保护的共享资源进行写操作时仍然可以继续读,而不必等待写者完成写操作,写者也不需要等待所有读者完成读操作才去进行写操作。但是,写者与写者之间仍然是互斥的,即如果有写者在进行写操作,其他写者必须自旋在那里,直到写者释放了顺序锁。

    这种锁有一个限制,它必须要求被保护的共享资源不含有指针,因为写者可能使得指针失效,但读者如果正要访问该指针,将导致OOPs。

    如果读者在读操作期间,写者已经发生了写操作,那么,读者必须重新读取数据,以便确保得到的数据是完整的。

    这种锁对于读写同时进行的概率比较小的情况,性能是非常好的,而且它允许读写同时进行,因而更大地提高了并发性。





    展开全文
  • 互斥锁使用std::mutex类;条件变量使用std::condition_variable类;自旋锁通过C++11的std::atomic类实现,使用“自旋”的CAS操作。 #include <thread> #include <mutex> #include <iostream> #...

    互斥锁、自旋锁、条件变量

    互斥锁使用std::mutex类;条件变量使用std::condition_variable类;自旋锁通过C++11的std::atomic类实现,使用“自旋”的CAS操作。
    自旋锁参考:C++11实现自旋锁

    #include <thread>
    #include <mutex>
    #include <iostream>
    #include <atomic>
    #include <condition_variable>
    using namespace std;
    
    // 使用C++11的原子操作实现自旋锁(默认内存序,memory_order_seq_cst)
    class spin_mutex {
        // flag对象所封装的bool值为false时,说明自旋锁未被线程占有。  
        std::atomic<bool> flag = ATOMIC_VAR_INIT(false);       
    public:
        spin_mutex() = default;
        spin_mutex(const spin_mutex&) = delete;
        spin_mutex& operator= (const spin_mutex&) = delete;
        void lock() {
            bool expected = false;
            // CAS原子操作。判断flag对象封装的bool值是否为期望值(false),若为bool值为false,与期望值相等,说明自旋锁空闲。
            // 此时,flag对象封装的bool值写入true,CAS操作成功,结束循环,即上锁成功。
            // 若bool值为为true,与期望值不相等,说明自旋锁被其它线程占据,即CAS操作不成功。然后,由于while循环一直重试,直到CAS操作成功为止。
            while(!flag.compare_exchange_strong(expected, true)){ 
                expected = false;
            }      
        }
        void unlock() {
            flag.store(false);
        }
    };
    
    int k = 2;
    // 实现互斥
    spin_mutex smtx; // 自旋锁,如果自旋锁已经被占用,调用者就一直循环检查自旋锁是否被解除占用。
    mutex mtx; // 互斥锁,如果互斥锁已经被占用,调用者这会进入睡眠状态,等待互斥锁解除占用时被唤醒。
    // 实现同步
    condition_variable cond;
    
    // 不加锁
    void print_without_mutex(int n){
        for(int i = 1; i <= n ; i++){
            cout << this_thread::get_id() << ": " << i << endl;
        }
    }
    // 使用互斥锁,实现互斥
    void print_with_mutex(int n){
        unique_lock<mutex> lock(mtx);
        for(int i = 1; i <= n ; i++){
            cout << this_thread::get_id() << ": " << i << endl;
        }
    }
    // 使用自旋锁,实现互斥
    void print_with_spin_mutex(int n){
        smtx.lock();
        for(int i = 1; i <= n ; i++){
            cout << this_thread::get_id() << ": " << i << endl; 
        }
        smtx.unlock();
    }
    // 使用条件变量,实现同步(需要与mutex配合使用),打印 1,1,2,2,3,3...
    void print_with_condition_variable(int n){
        unique_lock<mutex> lock(mtx);
        for(int i = 1; i <= n ; i++){
            while(!(i <= k/2)){  // 循环检查某个条件(i <= k/2)是否满足,满足则跳出循环,继续向下执行。
                cond.wait(lock); // 阻塞当前线程,等待lock对象封装的互斥锁mtx解除占用(收到解除占用互斥锁的线程的notify)
            }
            cout << this_thread::get_id() << ": " << i << endl; 
            k++;
            cond.notify_one(); // 随机唤醒一个等待的线程
        }
    }
    
    int main(){
        thread t1(print_with_spin_mutex,10);
        thread t2(print_with_spin_mutex,10);
        t1.join();
        t2.join();
        return 0;
    }
    

    读写锁

    C++17提供了shared_mutex来解决读者-写者问题,也就是读写锁。和普通锁不一样,读写锁同时只能有一个写者或多个读者,但不能同时既有读者又有写者,读写锁的性能一般比普通锁要好。

    shared_mutex 比一般的 mutex 多了函数 lock_shared() / unlock_shared(),允许多个(读者)线程同时加锁、解锁,而 shared_lock 则相当于共享版的 lock_guard。对 shared_mutex 使用 lock_guardunique_lock 就达到了写者独占的目的。

    一下代码源自:C++ 并发编程(七):读写锁(Read-Write Lock)

    #include <thread>
    #include <shared_mutex>
    #include <iostream>
    #include <vector>
    #include <unistd.h> // sleep(seconds), usleep(microseconds)
    using namespace std;
    
    
    class Counter {
    public:
      Counter() : value_(0) {
      }
    
      // Multiple threads/readers can read the counter's value at the same time.
      std::size_t Get() const {
        std::shared_lock<std::shared_mutex> lock(mutex_);
        return value_;
      }
    
      // Only one thread/writer can increment/write the counter's value.
      void Increase() {
        // You can also use lock_guard here.
        std::unique_lock<std::shared_mutex> lock(mutex_);
        value_++;
      }
    
      // Only one thread/writer can reset/write the counter's value.
      void Reset() {
        std::unique_lock<std::shared_mutex> lock(mutex_);
        value_ = 0;
      }
    
    private:
      mutable std::shared_mutex mutex_;
      std::size_t value_;
    };
    
    
    std::mutex g_io_mutex;
    
    void Worker(Counter& counter) {
      for (int i = 0; i < 3; ++i) {
        counter.Increase();
        std::size_t value = counter.Get();
    
        std::lock_guard<std::mutex> lock(g_io_mutex);
        std::cout << std::this_thread::get_id() << ' ' << value << std::endl;
      }
    }
    
    int main() {
      const std::size_t SIZE = 2;
    
      Counter counter;
    
      std::vector<std::thread> v;
      v.reserve(SIZE);
    
      v.emplace_back(Worker, std::ref(counter));
      v.emplace_back(Worker, std::ref(counter));
    
      for (std::thread& t : v) {
        t.join();
      }
    
      return 0;
    }
    

    输出结果:
    140561343293184 1
    140561343293184 3
    140561343293184 4
    140561334900480 2
    140561334900480 5
    140561334900480 6

    展开全文
  • 自旋互斥锁 读写锁 递归锁 自旋锁: 互斥锁: 1.自旋锁不会睡眠,互斥锁会睡眠,因此自旋锁效率高于互斥锁。------自旋锁比较适用于锁使用者保持锁时间比较短的情况 2.自旋锁消耗cpu:由于一直查询,所以自旋锁...

    自旋锁 互斥锁 读写锁 递归锁

    自旋锁:
    互斥锁:

    1.自旋锁不会睡眠,互斥锁会睡眠,因此自旋锁效率高于互斥锁。------自旋锁比较适用于锁使用者保持锁时间比较短的情况
    2.自旋锁消耗cpu:由于一直查询,所以自旋锁一直占用cpu,互斥锁不会,自旋锁导致cpu使用效率低
    3.自旋锁容易造成死锁-------比如递归调用

    递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。此外如果一个进程已经将资源锁定,那么,即使其它申请这个资源的进程不停地疯狂"自旋",也无法获得资源,从而进入死循环。

    读写锁:
    读锁:允许多个线程同时持有,进行读—
    写锁:只能有一个线程写
    但读写锁不能同时获取,即读的时候,不允许有写锁工作,写的时候不允许读锁工作

    展开全文
  • (1)互斥锁在访问共享资源之前对锁进行加锁操作,在访问完成之后对锁进行解锁操作。加锁后,任何视图访问加锁的线程...(2)读写锁(rwlock)(读模式共享,写模式互斥)三种状态:读加锁、写加锁、不加锁。一次只有...

    三种锁:

    (1)互斥锁

    在访问共享资源之前对锁进行加锁操作,在访问完成之后对锁进行解锁操作。

    加锁后,任何视图访问加锁的线程都会被阻塞,知道当前线程解锁。

    如果解锁时有一个以上线程阻塞,所有该锁上的线程都变为就绪状态。第一个变为就绪状态的线程又执行加锁操作,其它线程又会进入等待。只有一个线程可以访问被互斥锁保护的资源。

    (2)读写锁(rwlock)(读模式共享,写模式互斥)

    三种状态:读加锁、写加锁、不加锁。一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。

    写加锁模式下:任何试图对这个锁进行加锁的线程都会被阻塞,知道写进程对其进行解锁。

    读加锁模式下:任何线程都可以对其进行读加锁操作,但是所有试图进行写加锁操作的线程都会被阻塞,直到所有线程都解锁。

    解决读写不公平状态:当处于读模式的读写锁收到一个试图对其进行写模式加锁的操作时,会阻塞后面对其进行读模式加锁操作的线程。等到已经加锁读模式的锁解开后,写进程就可以访问保护资源。

    (3)自旋锁

    自旋锁的使用模式和互斥锁很类似。只是在加锁后,有线程试图再次执行加锁操作时,线程不会被阻塞,而处于循环等待的忙等状态(CPU不能做其它事情)。

    自旋锁适用情况:锁被持有时间较短,而且进程不希望在重新调度上话费太多时间。



    展开全文
  • 自旋锁和互斥锁的区别 POSIX threads(简称Pthreads)是在多核平台上进行并行编程的一套API。线程同步是并行编程中非常重要的通讯手段,其中最典型的应用就是用 Pthreads提供的锁机制(lock)来对多个线程之间的共享...
  • 1、自旋锁自旋锁最多可能被一个可执行线程所持有。一个被征用的自旋锁使得请求它的线程在等待重新可用时自旋(特别浪费处理器时间)。所以自旋锁不应该被长时间持有。自旋锁是不可递归的!(1)自旋锁相关函数用户态的...
  • POSIX threads(简称Pthreads)是在多核平台上进行并行编程的一套API。线程同步是并行编程中非常重要的通讯手段,其中最典型的应用就是用 Pthreads提供的机制(lock)来对多个线程...Mutex(互斥量):pthread_mutex_t...
  • 最常用的就是互斥锁,当然还有很多种不同的锁,比如自旋锁、读写锁、乐观锁等,不同种类的锁自然适用于不同的场景。 如果选择了错误的锁,那么在一些高并发的场景下,可能会降低系统的性能,这样用户体验就会非常差...
  • 互斥锁读写锁自旋锁。这三种锁的使用以及区别将在下面一步步深入了解。 1 互斥锁 1.1 互斥锁的作用 互斥锁(也称互斥量)可以用于保护关键代码段,以确保其独占式的访问,和有点像一个二进制信号量。当进入关键...
  • 当已经有一个线程加锁后,其他线程加锁则就会失败,互斥锁自旋锁对于加锁失败后的处理方式是不一样的: 互斥锁加锁失败后,线程会释放 CPU ,给其他线程; 自旋锁加锁失败后,线程会忙等待,直到它拿到锁; 互斥锁...
  • 互斥锁 mutex:在访问共享资源之前对进行加锁操作,在访问完成之后进行解锁操作。加锁后,任何其他试图再次加锁的线程会被阻塞,直到当前进程解锁。...读写锁 rwlock(也叫作共享互斥锁:读模式共享,写模...
  • 自旋锁与互斥锁的区别

    千次阅读 2019-07-21 17:33:47
    自旋锁和互斥锁的区别 POSIX threads(简称Pthreads)是在多核平台上进行并行编程的一套API。线程同步是并行编程中非常重要的通讯手段,其中最典型的应用就是用 Pthreads提供的锁机制(lock)来对多个线程之间的共享...
  • 互斥锁 当有一个线程要访问共享资源(临界资源)之前,会对线程访问的这段代码(临界区)进行加锁。如果在加锁之后没释放锁之前其他线程要对临界资源进行...读写锁 也叫做共享互斥锁,读模式共享,写模式互斥。有...
  • 自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。由于自旋锁使用者一般保持锁...
  • 这个问题不错,锁是一个常见的同步概念,我们都听说过加锁(lock)或者解锁(unlock),当然学术一点地说法是获取(acquire)和释放(release)。恰好pthread包含这几种锁的API,而C++11...也便是常说的互斥锁。尽管名称不含...
  • 自旋锁的效率高于互斥锁。 使用自旋锁时要注意: 由于自旋时不释放CPU,因而持有自旋锁的线程应该尽快释放自旋锁,否则等待该自旋锁的线程会一直在哪里自旋,这就会浪费CPU时间。 持有自旋锁的...
  • 自旋锁、互斥锁和信号量

    千次阅读 2018-10-09 11:26:29
    自旋锁 Linux内核中最常见的自旋锁(spin lock)。自旋锁最多只能被一个可执行线程持有。如果一个执行线程试图获得一个被已经持有的自旋锁,那么该线程就会一直进行忙循环——旋转——等待重新可用。要是未被...
  • 同步和互斥在多线程和多进程编程中是一个基本的需求,互相协作的多个进程和线程往往需要某种方式的同步和互斥。POSIX定义了一系列同步对象用于同步和互斥。 同步对象是内存中的变量属于进程中的资源,可以按照与访问...
  • 互斥锁读写锁自旋锁的区别

    千次阅读 2019-02-14 18:04:25
    读写锁特点: 读写锁有三种状态:读加锁状态、写加锁状态和不加锁状态  只有一个线程可以占有写状态的锁,但可以有多个线程同时占有读状态锁,这也是它可以实现高并发的原因。当其处于写状态锁下,...
  • 我曾经也傻傻分不清楚听说过的"互斥锁, 读写锁, 自旋锁", 所以现在决定再深入理解一下, 并且重点理解分布式锁的实现方案.互斥锁共享资源的使用是互斥的,即一个线程获得资源的使用权后就会将该资源加锁,使用完后会...
  • 互斥锁 在多线程的情况下,当一个变量可以被多个线程修改时,就需要考虑多线程同步问题。线程A修改变量前,先加锁,修改结束再解锁,然后线程B获取同样的锁,修改结束再解锁,如果不是同一把锁,同步是无效的。 在...
  • 前言 如何用好锁,也是程序员的基本素养之一了。...最常用的就是互斥锁,当然还有很多种不同的锁,比如自旋锁、读写锁、乐观锁等,不同种类的锁自然适用于不同的场景。 如果选择了错误的锁,那么在一些高并发的场
  • 互斥锁(mutexlock):最常使用于线程同步的锁;标记用来保证在任一时刻,只能有一个线程访问该对象,同一线程多次加锁操作会造成死锁;临界区和互斥量都可用来实现此锁,通常情况下锁操作失败会将该线程睡眠等待锁...
  • 1、互斥锁自旋锁 基础的两种就是互斥锁自旋锁,有很多⾼级的锁都是基于它们实现的,加锁的目的就是保证共享资源在任意时间⾥,只有⼀个线程访问,这样就可以避免多线程导致共享数据错乱的问题。 当一个线程加锁...
  • 读写锁特点: 1)多个读者可以同时进行读 2)写者必须互斥(只允许一个写者写,也不能读者写者同时进行) 3)写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者) 互斥锁特点: 一次...
  • 常用的有互斥锁自旋锁和读写锁自旋锁和互斥锁功在使用时差不多,每一时刻只能有一个执行单元占有锁,而占有锁的单元才能获得临界资源的使用权,从而达到了互斥的目的。自旋锁与互斥锁的区别在于:自旋锁在执行...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,586
精华内容 7,034
关键字:

自旋锁互斥锁读写锁