并发,互斥,锁。草稿

Mutex class

A mutex is a lockable object that is designed to signal when critical sections of code need exclusive access, preventing other threads with the same protection from executing concurrently and access the same memory locations.

mutex objects provide exclusive ownership and do not support recursivity (i.e., a thread shall not lock a mutex it already owns) -- see recursive_mutex for an alternative class that does.

It is guaranteed to be a standard-layout class.

Member types

member typedescription
native_handle_type Type returned by native_handle (only defined if library implementation supports it)

Member functions

(constructor)
Construct mutex (public member function )
lock
Lock mutex (public member function )
try_lock
Lock mutex if not locked (public member function )
unlock
Unlock mutex (public member function )
native_handle
Get native handle (public member function )
运用记忆中仅存的几条汇编记忆,试着写写 mutex的lock和unlock的底层实现机制。不知道对不对。先贴上来。
关于几个概念:
mutex:互斥量,在cpu一条原子指令中,完成互斥变量的操作。 多个互斥操作之间是互斥的。
互斥锁:并发中,使用互斥量保证单线程进行一段代码执行。否则挂起。 《千万别理解为原子操作》
并发:任何时候打断某线程任何非原子操作,切换到其他线程。
一致性:对于某数据修改,整个程序只有一个操作入口,并使用互斥机制,保证整段操作的单线程执行。才叫一致性。
(如果只是保证所有操作的互斥,并不能保证一致性。因为几个操作可以并发执行,好吧,不用写代码了。并发太恐怖了)
原子操做:人走5米,可以打断为走1米。还可以打断为左脚抬到半空中,什么是不可打断的,是原子操作呢?不可回撤的动作就是员操。我的脚不停使唤,你打断我。我还是要完成一条腿的抬起落地动作。就是原操。
cpu每条指令是原子的。不可回撤,如果cpu,把多条执行绑在一起组成特殊指令,并且不可回撤。那么以后应该会有很多复杂,组合动作的原操。
并发带来的问题:非独占数据,在并发中,计划被变化搞的面目前非。那么设定一个方案,阻止计划实行中,变化加入。 那么这个阻止方案,也必须阻止变化来修改阻止方案,那么又必须设定一个阻止变化 阻止变化的方案的方案。
无穷尽,就必须采取先斩后奏,研究方案和执行方案,一步完成,testandset。 
#include <iostream>       // std::cout
#include <atomic>         // std::atomic
#include <thread>         // std::thread
#include <vector>         // std::vector
#include <sstream>
#include <algorithm>
#include <memory>
#include <chrono>
#include <pthread.h>
#include <unistd.h>

using namespace std;
//

void mul(int a)
{
    cout<<"c++11 :"<<a*a<<endl;
    cout<<this_thread::get_id()<<endl;
}

//DWORD WINAPI mul2(LPVOID a)
//{
//    cout<<"winapi :"<<(int)a*(int)a<<endl;
//}

void mul3(int a)
{
    cout<<"unix :"<<a*a<<endl;
}

//void* unixFun(void *arg)
//{
//    mul3(*(int*)arg);
//    pid_t pid=getpid();
//    pthread_t tid=pthread_self();
//    int * value_ptr=new int(999);
//    //pthread_exit(value_ptr);
//    cout<<"pid:"<<pid<<".tid:"<<tid<<endl;
//    return ((void*)0);
//}

int main()
{

//    //win api
//    HANDLE hThread;
//    DWORD threadID;
//    hThread=CreateThread(NULL,0,mul2,(LPVOID)3,0,&threadID);

    //c++ 11
    thread  newt=thread(mul,3);
    newt.join();

//    //unix
//    //int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,(void*)(*start_rtn)(void*),void *arg);
//    pthread_t ntid;
//    int err;
//    int num=3;
//    int* rvalue;
//    err=pthread_create(&ntid,NULL,unixFun,&num);
//    pthread_join(ntid,(void**)&rvalue);
//    cout<<*rvalue<<endl;


    this_thread::sleep_for(chrono::seconds(6));
    return 0;
}

1.thread

有join和detach2种方式。等待和分离。2种方式都会导致joinable为false。(才能正常调用析构和move语义???)

//?线程结束了,怎么样了。猜测下。join模式,理论会回收,好像是暂时不动。不够才回收,detach,线程结束,由c++ 库保证资源的回收,因为可能主线程已经结束,所以之后应该是c++库和系统之间的交互了。

join是一种阻塞模式,会让调用者处于阻塞模式。完成后才会唤醒调用者。

在数据的访问问题。线程要么join,否则detach常规就是传递值类型。

join之后,代码中的thread对象就和线程没有关系了。所以不能再对thread对象再一次join了。detach测试也是一样。

所以看来。thread对象是对象。但是生命周期和线程所分配的栈并不是一个生命周期。应该就像定义了一个connect类的对象。但是如果使用对象的del命令。那么还想使用对象的search方法,是不可能的一样的道理吧。

join或detach,一旦执行,那么thread对象和thread堆栈就没有了任何联系。

 detach 之后,线程也叫做,daemon线程。守护线程。

主程序结束,必须保证所有的线程对象已经join或者detach,否则线程对象析构会调用std::taminate方法。也就是abort。中断程序。

请注意,std::thread的析构函数调用std::terminate的问题并不真的和异常相关。当我们无意识的忘记调用(线程)合并或者分离的时候,它同样会被触发:

“不要用在程序里用裸线程:用类似RAII封装器去代替它”。

http://www.ituring.com.cn/article/17979

确保成员函数不会传出指针或引用的同时,检查成员函数是否通过指针或引用的方式来调用也是很重要的(尤其是这个操作不再你哦控制之下时)。

mutex,互斥量,不要裸使用。简单就用lock_guard,lock一直到它的生命周期。.  如果是要和条件变量配合使用。那么就用unique_lock。因为unique_lock可以手动lock和unlock。wait是有条件的lock和unlock。所以编译器不会编译一个使用guard的wait。

mutex的raii类(unique_lock),加上条件变量,基本应该可以满足大部分常规需求。因为条件变量就相当于单线程的事件。虽然没有那么精准(nodifyone是随机。notify_all是所有)。

用多线程,也就是打乱了之前单线程的函数执行顺序和共享变量的一致性。

共享变量的一致性,用mutex的资源管理类来保证。保证某个临界区(共享变量的读写)只有单个线程访问。

而函数执行顺序。通过分析,先行函数的改变,利用先行函数的改变,作为后继函数的条件变量,并使用notify,来保证性能,来串行化各个函数的执行语句。

 基本理论是ok。但确实实施不简单,除非是固定的现实模式。稍微复杂一点。自己还是感觉为什么自己的大脑不多几个线程。。。。

条件变量用于某个线程需要在某种条件成立时才去保护它将要操作的临界区,这种情况从而避免了线程不断轮询检查该条件是否成立而降低效率的情况,这是实现了效率提高。。。在条件满足时,自动退出阻塞,再加锁进行操作。

原文地址:https://www.cnblogs.com/lsfv/p/6284735.html