如何查看线程id?
实现三个线程分别打印ABC(有序)
代码实现如下:
运行结果如下:
多线程中进行fork会发生什么情况?
注意:不管在哪个线程打印pid,pid的值都是整个进程的id号。
运行结果如下
1、创建一个线程,有两条路径,一条是主线程,一条是子线程,在主线程加入fork();
运行结果如下
我们可以得知:父进程的id号为4952,子进程的id号为4954,父进程的id号在主线程和子线程都打印了,子进程的id号只在主线程打印
4953就是子线程的id号
我们发现fork之后,子进程只有一条路径
2、创建一个线程,有两条路径,一条是主线程,一条是子线程,在子线程加入fork();
运行结果如下
我们得知:父进程的id号为4976,子进程的id号为4978
子进程的id号只在子线程打印,父进程的id号在主线程和子线程都打印
总结:
不管父进程有多少条执行路径,fork后的子进程只有一条执行路径,子进程只启动fork所在的那条执行路径!
多线程fork,引入互斥锁
运行结果如下:
子线程打印锁了,锁住和解锁了,子进程一直没有得到锁,阻塞住,结束不了,父进程也不能退出
为什么子线程释放锁了,子进程依然无法得到锁
结论:fork之后,父进程和子进程用的不是同一个锁!父进程修改锁的状态,子进程依然无法进行加锁。fork之后,子进程复制了父进程的资源,同时也把锁复制过来,这把锁的状态取决于复制的那一刻父进程锁的状态。
sleep(1);子线程加锁成功,fork之后子进程拿到的锁是已经加锁的状态,再加锁,阻塞住。
解决方法
不能一上来就unlock,表面上看我们是在进行加锁和解锁,实际上加锁和解锁背后是对临界资源的控制,当它加了锁,说明临界资源在被使用,没有依据的情况下去解锁是不可取的。
fork之后 并不确定锁的状态
我们就挑一个空闲的时间,没人用锁的时候,再去进行fork去控制
加锁能成功,说明没人用锁。
然后fork之后在父子进程中分别进行解锁就可以了
第一个参数是在执行fork之前执行的
第二个参数是fork以后在父进程进行的
第三个参数是fork以后在子进程进行的
等父进程中锁没人用的时候再进行fork,所以38行的fork会延迟
运行结果如下
条件变量的使用
条件变量提供了一种线程间的通知机制:当某个共享数据达到某个值的时候,唤醒等待这个共享数据的线程
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);
//初始化条件变量
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
//等待条件变量的值达到某个条件//阻塞
int pthread_cond_signal(pthread_cond_t *cond);
//唤醒单个线程
int pthread_cond_broadcast(pthread_cond_t *cond);
//唤醒所有等待的线程
int pthread_cond_destroy(pthread_cond_t *cond);
//销毁条件变量
使用条件变量就要用到互斥锁
因为我们要添加到条件变量的队列中,在添加的过程中,不能让其他访问。
防止两个人同时把自己添加到条件变量队列上
fun线程先加锁操作,另一个线程加锁失败。
fun线程先加锁操作,然后添加队列的过程中,添加完,解个锁,阻塞住,加进去之后其他线程才能添加。然后加个锁,出队列,出队列以后我们再解锁。就是wait阻塞住,等待全局变量可用,主线程通知变量可用了,wait解除阻塞。
唤醒的时候,必须是没人入出队列。唤醒之前 加个锁,说明其他人没人用锁没人进出队列
运行结果如下
唤醒的是哪个线程我们不清楚
该进代码
运行结果如下
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
pthread_mutex_t mutex;
pthread_cond_t cond;
void * fun1(void * arg)
{
char* s = (char*)arg;
while( 1 )
{
//阻塞,被唤醒
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
printf("fun1 read:%s\n",s);
if (strncmp(s,"end",3) == 0 )
{
break;
}
}
}
void * fun2(void * arg)
{
char* s = (char*)arg;
while( 1 )
{
//阻塞,被唤醒
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
printf("fun2 read:%s\n",s);
if ( strncmp(s,"end",3) == 0 )
{
break;
}
}
}
int main()
{
pthread_t id[2];
char buff[128] = {
0};
pthread_cond_init(&cond,NULL);
pthread_mutex_init(&mutex,NULL);
pthread_create(&id[0],NULL,fun1,(void*)buff);
pthread_create(&id[1],NULL,fun2,(void*)buff);
while( 1 )
{
fgets(buff,128,stdin);
if ( strncmp(buff,"end",3) == 0 )
{
pthread_mutex_lock(&mutex);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
break;
}
else
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
}
pthread_join(id[0],NULL);
pthread_join(id[1],NULL);
exit(0);
}
运行结果如下
最多可以创建多少个线程?
32位系统下是0-4G,用户可以使用下3G,内核使用上面那1G,不管多少线程,代码段数据段是共享的,线程栈不共享
3G除以一个线程大小,大概可以算出理论上创建多少个线程
sudo设置线程栈的大小
线程栈的大小10M
线程数目4096
期望创建2000个
3G/10M 300个左右的线程
栈缩小一半
可以创建约600个线程
因为系统本身还运行着一些进程
要防止栈溢出,或者设置太大
深度系统可以创建1万多个线程
读写锁
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *rwlock, pthread_rwlockattr_t *attr);
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)