• Go 语言协程的一个坑


    如下同样逻辑的代码,用 C++ 实现与用 Go 实现结果完全不一样. C++ 代码能够正常运行,而 Golang 代码直接死锁(即使强制调度也是死锁).

    简单分析:
    C/C++ 锁粒度是线程级别,线程调度由内核提供. Golang 锁粒度为协程级别,多个协程的底层运行可能会在同一个M上,而且协程调度会导致执行一半的代码被挂起,会导致两个协程同时满足条件进入等待,从而产生死锁.

    package main
    
    import (
    	"runtime"
    	"sync"
    	"time"
    )
    
    var flag bool = true
    var wg = &sync.WaitGroup{}
    var cond = sync.NewCond(&sync.Mutex{})
    
    func test_cond(idx int, v bool) {
    	for i := 0; i < 3; i++ {
    		cond.L.Lock()
    		defer cond.L.Unlock()
    
    		for flag == v {
    			println(idx, "is waitting")
    			cond.Wait()
    			println(idx, "finish waitting")
    		}
    
    		flag = !flag
    		println("routine", idx, "is running")
    		time.Sleep(time.Millisecond * 100)
    		cond.Broadcast()
    		runtime.Gosched()
    	}
    	wg.Done()
    	println("routine", idx, "quit")
    }
    
    func main() {
    	wg.Add(2)
    	go test_cond(0, true)
    	go test_cond(1, false)
    
    	wg.Wait()
    }
    

    自己仿照 Golang 的 WaitGroup 的实现.

    #ifndef _WAIT_GROUP_HPP
    #define _WAIT_GROUP_HPP
    
    namespace std
    {
    #include <condition_variable>
    #include <mutex>
    
        class wait_group
        {
        private:
            mutex _m;
            condition_variable _cond;
            int _wait_count;
    
        public:
            wait_group(int count = 0) : _wait_count(count) {}
    
            ~wait_group() {}
    
            void add(int count)
            {
                lock_guard<std::mutex> _lk(_m);
                _wait_count += count;
            }
    
            void set(int count)
            {
                lock_guard<std::mutex> _lk(_m);
                _wait_count = count;
            }
    
            void wait()
            {
                unique_lock<std::mutex> _lk(_m);
                while (_wait_count)
                {
                    _cond.wait(_lk);
                }
                _cond.notify_all();
            }
    
            int wait_for(int milliseconds)
            {
                unique_lock<std::mutex> _lk(_m);
                while (_wait_count)
                {
                    auto ret = _cond.wait_for(_lk, chrono::milliseconds(milliseconds));
                    if (ret == cv_status::timeout)
                        return _wait_count;
                }
    
                _cond.notify_all();
                return 0;
            }
    
            void done()
            {
                lock_guard<std::mutex> _lk(_m);
                if (_wait_count)
                    _wait_count--;
    
                if (!_wait_count)
                    _cond.notify_all();
            }
        };
    };
    
    #endif //_WAIT_GROUP_HPP
    
    #include <iostream>
    
    #include <mutex>
    #include <condition_variable>
    #include <chrono>
    #include <thread>
    
    #include "wait_group.hpp"
    
    using namespace std;
    
    condition_variable cond;
    mutex m;
    
    wait_group wg;
    bool flag = true;
    
    void test_cond(int idx, bool wv)
    {
        for (int idx = 0; idx < 3; idx++)
        {
            std::unique_lock<std::mutex> _lk(m);
            while (flag == wv)
            {
                if (cond.wait_for(_lk, chrono::seconds(10)) == cv_status::timeout)
                    cerr << "timeout" << endl;
            }
    
            flag = !flag;
            cerr << "thread: " << idx << " is running" << endl;
            this_thread::sleep_for(chrono::milliseconds(100));
            cond.notify_all();
        }
    
        wg.done();
        cerr << "thread: " << idx << " quit" << endl;
    }
    
    int main()
    {
        wg.set(2);
    
        thread t1(test_cond, 0, true);
        t1.detach();
        thread t2(test_cond, 1, false);
        t2.detach();
    
        wg.wait();
    
        return 0;
    }
    

  • 相关阅读:
    Java Android程序员软件开发知识:枚举的介绍,以及代码的编写教程。
    Android中实现全屏、无标题栏的两种办法(另附Android系统自带样式的解释)
    Android(java)开发之将double类型,强制保留到小数点后两位解决方法。
    Android开发之第三方推送JPush极光推送知识点详解 学会集成第三方SDK推送
    Android开发之清除缓存功能实现方法,可以集成在自己的app中,增加一个新功能。
    输入流、输出流
    关键字和继承
    java集合
    SpringMVC框架拦截器
    SpringMVC框架基础
  • 原文地址:https://www.cnblogs.com/sinpo828/p/14694939.html
Copyright © 2020-2023  润新知