//TMutex和TCondition进程共享模式,不可用做非亲缘关系进程间通讯,容易引起死锁
class TMutex
{
public:
friend class TCondition;
class Lock
{
public:
explicit Lock(TMutex &mutex) : m_Mutex(mutex) { m_Mutex.Enter(); }
~Lock() { m_Mutex.Leave(); }
private:
TMutex &m_Mutex;
};
//isshared为true时,可在进程间同步,此时TMutex对象本身必须分配在共享内存上
TMutex(bool isshared = false) : m_Isshared(isshared)
{
pthread_mutexattr_t attr;
::pthread_mutexattr_init(&attr);
::pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
if (isshared)
::pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
::pthread_mutex_init(&m_Mutex, &attr);
}
~TMutex() { ::pthread_mutex_destroy(&m_Mutex); }
void Enter() { ::pthread_mutex_lock(&m_Mutex); }
void Leave() { ::pthread_mutex_unlock(&m_Mutex); }
private:
pthread_mutex_t m_Mutex;
bool m_Isshared;
};
//TMutex和TCondition进程共享模式,不可用做非亲缘关系进程间通讯,容易引起死锁
class TCondition
{
public:
explicit TCondition(TMutex& mutex) : m_Mutex(mutex)
{
pthread_condattr_t attr;
::pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
if (mutex.m_Isshared)
::pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
::pthread_cond_init(&m_Cond, &attr);
}
~TCondition() { ::pthread_cond_destroy(&m_Cond); }
public:
void Wait() { ::pthread_cond_wait(&m_Cond, &m_Mutex.m_Mutex); }
void Wait(const int seconds, const int nanoseconds = 0)//seconds秒数,nanoseconds纳秒数,1秒 = 1000 * 1000 * 1000纳秒
{
timespec abstime;
clock_gettime(CLOCK_MONOTONIC, &abstime);
//clock_gettime(CLOCK_REALTIME, &abstime);
abstime.tv_sec += seconds;
abstime.tv_nsec += nanoseconds;
if(abstime.tv_nsec >= NANO_SEC_MAX)
{
++abstime.tv_sec;
abstime.tv_nsec -= NANO_SEC_MAX;
}
::pthread_cond_timedwait(&m_Cond, &m_Mutex.m_Mutex, &abstime);
}
void Notify() { ::pthread_cond_signal(&m_Cond); }
void NotifyAll() { ::pthread_cond_broadcast(&m_Cond); }
private:
static const long NANO_SEC_MAX = 1000000000L;
pthread_cond_t m_Cond;
TMutex& m_Mutex;
};
定义了一个类 :锁TMutex
TMutex &m_Mutex;
内部类LOCK
explicit 构造函数:
explicit Lock(TMutex &mutex) : m_Mutex(mutex)
{ m_Mutex.Enter(); }
构造函数中对这个传入的锁变量,赋值给TMUTEX类中的变量,同时进入enter函数
够函数汇总调用leave函数
void Enter() { ::pthread_mutex_lock(&m_Mutex); }
void Leave() { ::pthread_mutex_unlock(&m_Mutex); }
调用pthread库中的lock,将m_Mutex,类TMutex的引用进行lock
pthread_mutex_t m_Mutex;,是一个标准的线程锁变量
TMutex(bool isshared = false) : m_Isshared(isshared)
TMutex的构造函数中,isshared一个站位参数。m_Isshared变量也被此赋值为false
isshared写定位false //isshared为true时,可在进程间同步,此时TMutex对象本身必须分配在共享内存上
TMutex的构造函数中定义了一个锁变量, pthread_mutexattr_t attr;
pthread_mutexattr_init() 函数成功完成之后会返回零,其他任何返回值都表示出现了错误。
函数成功执行后,互斥锁被初始化为未锁住态。
::pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);是一个全局函数
有时候带上::,有时候不带::。
这里调用函数前加"::",代表调用的是全局函数,不是类自己的成员函数
pthread_t 声明线程ID
static 静态,全局。共享内存取。
2.线程调用到函数在一个类中,那必须将该函数声明为静态函数函数
因为静态成员函数属于静态全局区,线程可以共享这个区域,故可以各自调用。
1 2 |
|
返回成功时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于指定各种不同的线程属性。新创建的线程从start_rtn函数的地址开始运行,该函数只有一个万能指针参数arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg的参数传入。
参数
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
最后一个参数是运行函数的参数。
在.h文件之中对函数进行定义,这个就是内联函数。
void *status;// 空类型指针,也就是任何类型指针
1. pthread_attr_t attr; //线程属性结构体,创建线程时加入的参数
2. pthread_attr_init( &attr ); //初始化
3.pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE )//给属性变量赋值
4. int ret = pthread_create( &tids[i], &attr, say_hello, ( void* )&( indexes[i] ) );//创建线程
5. pthread_attr_destroy( &attr ); //释放属性变量内存
6.int ret = pthread_join( tids[i], &status ); //主程序join每个线程后取得每个线程的退出信息status
互斥锁、互斥变量、条件变量
int sum = 0; //定义全局变量,让所有线程同时写,这样就需要锁机制
pthread_mutex_t sum_mutex; //互斥锁
全局两个
}
void * threadcallfunc(void *qwe)
{
pthread_mutex_lock(&sum_mutex);
sum+=*((int*)qwe);
pthread_mutex_unlock(&sun_mutex);
pthread_exit(0)}
在线程调用的函数之中,将一个之前外部声明过的一个锁变量进行锁住
然后进行操作,操作完成之后对这个变量进行解锁的操作。
在进行属性变量初始化之后要对锁变量进行初始化
带有信号量:
pthread_mutex_t tasks_mutex; //互斥锁
pthread_cond_t tasks_cond; //条件信号变量,处理两个线程间的条件关系
定义两个全局变量,一个是互斥变量,一个是条件变量
pthread_t pid = pthread_self(); //获取当前线程id
在线程1中进行操作,每次操作都需要加锁解锁,在条件达到时候,
pthread_cond_signal( &tasks_cond ); //signal:向hello1发送信号,表明已经>5
这个全局的信号改变,线程2 之中的
pthread_cond_wait( &tasks_cond, &tasks_mutex ); //wait:等待信号量生效,接收到信号,向hello2发出信号,跳出wait,执行
这个wait函数就会激活,继续执行。
pthread_attr_t attr; //线程属性结构体,创建线程时加入的参数
pthread_attr_init( &attr ); //初始化
pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); //是设置你想要指定线程属性参数,这个参数表明这个线程是可以join连接的,join功能表示主程序可以等线程结束后再去做某事,实现了主程序和线程同步功能
pthread_cond_init( &tasks_cond, NULL ); //初始化条件信号量
pthread_mutex_init( &tasks_mutex, NULL ); //初始化互斥量
pthread_t tid1, tid2; //保存两个线程id
//***
pthread_join( tid1, NULL ); //连接两个线程
pthread_join( tid2, NULL );
pthread_attr_destroy( &attr ); //释放内存
pthread_mutex_destroy( &tasks_mutex ); //注销锁
pthread_cond_destroy( &tasks_cond ); //正常退出
1.线程对象需要一个可调用物,而调用函数的对象默认是拷贝传参,因此要求可调用物和参数类型都支持拷贝构造。所以如果希望传递给线程引用值就要使用ref库进行包装,同时必须保证被引用对象在线程执行期间一直存在,否则会引发为定义行为。
2.线程对象在析构时会调用std::terminate 结束线程的执行,并不会关心线程是否执行完毕,所以要保证函数正确运行完毕必须调用join()等待线程执行函数或者调用detach()分离线程体。
3.Thread实例的析构会导致其表示线程强制终止?
可是测试证实不会!他是在线程中插入结束点等待它自动结束?若线程死锁或者啥的则不会结束,资源也不会释放。
4.Thread对象创建完毕就会执行,不提供Start(),但事实上它含有私有成员函数Start_thread()启动自己。
主线程做自己的事情,生成2个子线程,task1为分离,任其自生自灭,而task2还是继续送外卖,需要等待返回。(因该还记得前面说过僵尸进程吧,线程也是需要等待的。如果不想等待,就设置线程为分离线程)
额外的说下,linux下要编译使用线程的代码,一定要记得调用pthread库。
可重入:概念基本没有比较正式的完整解释,但是它比线程安全要求更严格。根据经验,所谓“重入”,常见的情况是,程序执行到某个函数foo()时,收到信号,于是暂停目前正在执行的函数,转到信号处理函数,而这个信号处理函数的执行过程中,又恰恰也会进入到刚刚执行的函数foo(),这样便发生了所谓的重入。此时如果foo()能够正确的运行,而且处理完成后,之前暂停的foo()也能够正确运行,则说明它是可重入的。
要确保函数线程安全,主要需要考虑的是线程之间的共享变量。属于同一进程的不同线程会共享进程内存空间中的全局区和堆,而私有的线程空间则主要包括栈和寄存器。因此,对于同一进程的不同线程来说,每个线程的局部变量都是私有的,而全局变量、局部静态变量、分配于堆的变量都是共享的。在对这些共享变量进行访问时,如果要保证线程安全,则必须通过加锁的方式。
多进程是立体交通系统,虽然造价高,上坡下坡多耗点油,但是不堵车。
多线程是平面交通系统,造价低,但红绿灯太多,老堵车。
notify():
唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait
方法,在对象的监视器上等待。
直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。
notifyAll():
唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait
方法,在对象的监视器上等待。
直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。