linux线程笔记

概念 

线程被称为轻量级进程。进程是资源分配的最小单位,而线程是计算机中独立运行,cpu调度的最小单元。一个程序包含一个或多个进程,一个进程包含一个或多个线程。线程比进程具有更高的并发性

                       

线程与进程相比的优势

线程占用资源少

多线程间共享地址空间,切换效率高

通信方面,线程之间通信更加方便和省时

提高应用程序的响应速度

 线程基本操作

名称说明
pthread_create创建新线程
pthread_self获取线程ID
pthread_exit线程退出
pthread_join线程等待

实例

     pthread_create

     pthread_self

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>


void *thread_func()
{
	
	pthread_t new_thread;
        new_thread = pthread_self();
	printf("thread id %u",(unsigned int)new_thread);
	printf("----end-----\n");
}

int main(int argc, char* argv[])
{
	int rtn;
	pthread_t thread_id;
	rtn = pthread_create(&thread_id,NULL,&thread_func,NULL);
	if(rtn!=0)
	{ 
           perror("create error");
	   exit(1);
	}
	sleep(1);
	return 0;

}

若主线程返回或调用exit退出,则整个进程将会终止,进程中的所有线程也将终止。故主线程不能过早从main函数退出,因此在main函数中使用sleep,使主线程休眠一段时间,等待新创建的线程结束后再退出。

     pthread_join

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>


void *thread_func1(void *ptr)
{
	while(1)
	{
		char *message;
		message = (char *)ptr;
		pthread_t new_thread;
		
		new_thread = pthread_self();
		printf("thread id %u\t messgae %s\n",(unsigned int)new_thread,message);
		sleep(1);

	}
}

void *thread_func2(void *ptr)
{
	while(1)
	{
		char *message;
		message = (char *)ptr;
		pthread_t new_thread;
		
		new_thread = pthread_self();
		printf("thread id %u\t messgae %s\n",(unsigned int)new_thread,message);
		sleep(1);
	}
}

int main(int argc, char* argv[])
{
	int rtn1,rtn2;
	pthread_t thread_id1;
	pthread_t thread_id2;
	char* message1 = "this is test 1 !!!";
	char* message2 = "this is test 2 !!!";
	rtn1 = pthread_create(&thread_id1,NULL,&thread_func1,(void*)message1);

	if(rtn1!=0)
	{ 
           perror("create error");
	   exit(1);
	}
	rtn2 = pthread_create(&thread_id2,NULL,&thread_func2,(void*)message2);
	if(rtn2!=0)
	{ 
           perror("create error");
	   exit(1);
	}

	pthread_join(thread_id1,NULL);
	pthread_join(thread_id2,NULL);
	printf("thread_id2 %d",rtn1);
	printf("thread_id2 %d",rtn2);

	return 0;

}

调用pthread_join函数的线程将被挂起,主线程调用函数pthread_join来等待新线程结束,所有主线程不会过早退出,当两个新线程全部结束后,主线程才会继续运行。

      pthread_exit

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>


void *thread_func1(void *ptr)
{
     while(1)
	 {
		char *message;
		message = (char *)ptr;
		pthread_t new_thread;
		
		new_thread = pthread_self();
		printf("thread id %u\t messgae %s\n",(unsigned int)new_thread,message);
		sleep(1);
	 }

}

void *thread_func2(void *ptr)
{
    while(1)
	{
		char *message;
		message = (char *)ptr;
		pthread_t new_thread;
		
		new_thread = pthread_self();
		printf("thread id %u\t messgae %s\n",(unsigned int)new_thread,message);
		sleep(1);
	}

}

int main(int argc, char* argv[])
{
	int rtn1,rtn2;
	pthread_t thread_id1;
	pthread_t thread_id2;
	char* message1 = "this is test 1 !!!";
	char* message2 = "this is test 2 !!!";
	rtn1 = pthread_create(&thread_id1,NULL,&thread_func1,(void*)message1);

	if(rtn1!=0)
	{ 
           perror("create error");
	   exit(1);
	}
	rtn2 = pthread_create(&thread_id2,NULL,&thread_func2,(void*)message2);
	if(rtn2!=0)
	{ 
           perror("create error");
	   exit(1);
	}

	printf("thread_id1 %d\n",rtn1);
	printf("thread_id2 %d\n",rtn2);
	pthread_exit(0);

	return 0;

}

主线程调用pthread_exit仅仅使主线程消亡,进程不会结束,进程中的线程也不会终止。上述可以看到打印了

    printf("thread_id1 %d\n",rtn1);
    printf("thread_id2 %d\n",rtn2);

之后两个线程任然在运行

线程间通信 

线程间共享进程的地址空间,因此线程间通信的难点在于对共享资源访问时的同步和互斥。线程等待函数pthread_join使线程挂起,等待另一线程结束后再运行,可以解决简单的线程同步。

名称说明
pthread_mutex_init初始化互斥锁
pthread_mutex_destory销毁互斥锁
pthread_mutex_lock对互斥锁加锁
pthread_mutex_unlock对互斥锁解锁
pthread_mutex_trylock尝试加锁,若不成功,则立即返回
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int count = 0;

void *thread_func1(void *ptr)
{
	while(1)
	{
	
		char *message;
		message = (char *)ptr;
		pthread_t new_thread;
		
		new_thread = pthread_self();
		printf("thread id %u\t messgae %s\n",(unsigned int)new_thread,message);
		sleep(1);
		pthread_mutex_lock(&mutex);
		count--;
		printf("count--        %d\n",count);
		printf("----end-----\n");
		pthread_mutex_unlock(&mutex);
	}
}

void *thread_func2(void *ptr)
{
	while(1)
	{
	
		char *message;
		message = (char *)ptr;
		pthread_t new_thread;
		
		new_thread = pthread_self();
		printf("thread id %u\t messgae %s\n",(unsigned int)new_thread,message);
		sleep(1);
		pthread_mutex_lock(&mutex);
		count++;
		printf("count++        %d\n",count);
		printf("----end-----\n");
		pthread_mutex_unlock(&mutex);
	}
}

int main(int argc, char* argv[])
{
	int rtn1,rtn2;
	pthread_t thread_id1;
	pthread_t thread_id2;
	char* message1 = "this  is test 1\n";
	char* message2 = "this  is test 2\n";
	rtn1 = pthread_create(&thread_id1,NULL,&thread_func1,(void*)message1);

	if(rtn1!=0)
	{ 
           perror("create error");
	   exit(1);
	}
	rtn2 = pthread_create(&thread_id2,NULL,&thread_func2,(void*)message2);
	if(rtn2!=0)
	{ 
           perror("create error");
	   exit(1);
	}

	pthread_join(thread_id1,NULL);
	pthread_join(thread_id2,NULL);
	printf("thread_id2 %d",rtn1);
	printf("thread_id2 %d",rtn2);

	return 0;

}

此例中有两个线程并发执行,对全局变量count进行互斥访问,一个count自增,一个count自减。其中一个线程调用pthread_mutex_lock函数进行加锁,另一个线程访问时,调用pthread_mutex_lock函数加锁时,发现互斥锁mutex已经被前一个线程锁住,该线程发生阻塞,直到前一个线程进行解锁,才会被唤醒,继续访问count全局变量。实现了对临界资源count的同步,互斥效果


 

名称说明
pthread_rwlock_init初始化读写锁
pthread_rwlock_destory销毁读写锁
pthread_rwlock_rdlock对读写锁加读锁
pthread_rwlock_wrlock对读写锁加写锁
pthread_rwlock_tryrdlock尝试加读锁,若不成功,立刻返回
pthread_rwlock_tyrwrlock尝试加写锁,若不成功,立刻返回
pthread_rwlock_unlock对读写锁解锁

例子:

  1. 允许多个reader同时读一个文件
  2. 当一个reader在读文件时,不允许writer写文件
  3. 当一个writer在写文件时,不允许reader读文件,也不允许其他writer写文件
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>

#define NUM 100

void ReadFile(int id);
void WriteFile(int id);
void RandomSleep();
void *Reader(void* id);
void *Writer(void* id);
int read_num = 0;
int write_num = 0;
pthread_rwlock_t rwlock;

void RandomSleep()
{
	struct timespec tv;
	tv.tv_sec = 0;
	tv.tv_nsec = (long)(rand()*1.0/RAND_MAX*999999999);
	nanosleep(&tv,NULL);
}
void ReadFile(int id)
{
	printf("read id :%d\t read num: %d\t write_num: %d\n",id,read_num,write_num);
	RandomSleep();
}
void WriteFile(int id)
{
	printf("write id :%d\t read num: %d\t write_num: %d\n",id,read_num,write_num);
	RandomSleep();
}

void *Reader(void* id)
{
	RandomSleep();
	pthread_rwlock_rdlock(&rwlock);
	read_num++;
	ReadFile(*((int*)id));
	read_num--;
	pthread_rwlock_unlock(&rwlock);
}

void *Writer(void* id)
{
	RandomSleep();
	pthread_rwlock_wrlock(&rwlock);
	write_num++;
	WriteFile(*((int*)id));
	write_num--;
	pthread_rwlock_unlock(&rwlock);	
}


int main(int argc, char* argv[])
{
	int id[NUM];
	int i;
	pthread_t readthread[NUM];
	pthread_t writethread[NUM];

    for(i = 0; i < NUM; i++)
	{
		id[i] = i;
	   pthread_create(&readthread[i],NULL,Reader,(void *)&id[i]);
	   pthread_create(&writethread[i],NULL,Writer,(void *)&id[i]);
	}
	for(i = 0; i < NUM; i++)
	{
	   pthread_join(readthread[i],NULL);
	   pthread_join(writethread[i],NULL);
	}
    pthread_exit(0);

	return 0;

}

......

线程私有数据相关函数

名称说明
pthread_key_create创建键值
pthread_setspecific设置线程的私有数据
pthread_getspecific得到线程的私有数据
pthread_key_delete删除键值

 

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>

pthread_key_t key;

void *thread_func2(void* agr)
{
	int val = 5;
	printf("thread_func2 is running id %d\n",(unsigned int)pthread_self());
	pthread_setspecific(key,(void*)(&val));
	printf("thread_func2 return %d\n",*((int*)pthread_getspecific(key)));
}

void *thread_func1(void* agr)
{
	int val = 10;
	pthread_t t2;
	printf("thread_func1 is running id %d\n",(unsigned int)pthread_self());
 	pthread_setspecific(key,(void*)(&val));	 
	pthread_create(&t2,NULL,thread_func2,NULL);
	sleep(3);
	printf("thread_func1 return %d\n",*((int*)pthread_getspecific(key)));
}

int main(int argc, char* argv[])
{
	pthread_t t1;


    printf("begin....\n");
   
	pthread_key_create(&key,NULL);
	pthread_create(&t1,NULL,thread_func1,NULL);
	sleep(5);
	printf("end....\n");
   
   
    pthread_exit(0);

	return 0;

}

创建的线程thread1和thread2,分别设置该键值所对应的私有数据,两个线程的私有数据互不相干

 

后记:

【1】线程
(1)线程的定义:
    每个用户进程有自己的独立的地址空间,task_struct 和地址空间映射表
    一起用来表示一个进程。为了提高系统的性能,许多操作系统规范里引入了轻量级进程的概念,
    也被称为线程。
(2)线程的特点:
    1、一个进程中的所有线程可以访问该进程的组成部件,如内存、全局变量------线程在进程的内部
    2、线程拥有自己独立的的堆栈和局部变量。(线程ID)
    3、线程的创建速度快、系统资源开销小。
    4、因此线程有时被称为轻量级进程(Lightweight Process,LWP
(3)线程与进程的关系
    逻辑:
    --- 一个进程至少有一个线程,称为主线程.----子线程由主线程创建出来的
    --- 其他所有线程都是依附于主线程(进程)存在的------》主线程在,子线程在,主线程无,子线程无。
    结论:进程之间空间独立,互不影响
          线程共享进程空间,主线程影响各线程。
    空间:
    --- 进程间内存相对独立,一个进程崩溃后,一般不会影响其它进程。
    --- 线程没有自己的地址空间,主线程死掉其它线程立即结束;
    效率:
    --- 进程在切换时,耗费资源较大,效率较低
    --- 线程属于轻量级进程,效率比较高。
(4)线程的创建
    pthread_create
    #include <pthread.h>
    int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);
    函数的功能:创建(开启)一个新的子线程(在主线程的基础上)
    参数:thread 线程id 
          attr 默认分配线程的堆栈空间以及属性信息,一般传NULL
          start_ruotine 线程的执行体
          void *(*start_routine) (void *)
          典型的函数指针:也是一个回调函数(***)
          注意:*start_routine 传一个地址 一般函数名可以作为地址传入
          arg 为第3个参数传参
    返回值:成功返回0,失败-1
    pthread_self
    pthread_t pthread_self(void);
    函数的功能:获取当前线程的id
    参数:无
    返回值:线程id
    pthread_exit
    void pthread_exit(void *retval);
    函数的功能:结束一个线程,并保存线程的状态
    参数:retval 用于保存线程结束时的状态信息
    返回值:无
    pthread_join
    int pthread_join(pthread_t thread, void **retval);
    函数的功能:回收子线程的资源
    参数:thread 指定子线程id
         retval 保存线程结束时状态
    返回值:成功返回0,失败-1
(5)线程间的通信方式-全局变量
    结论:因为多个线程共享进程空间,所以我们通过全局变量变量通信。
          当主线程启动起来,在当前进程内部多个线程抢占cpu
【2】线程间的同步互斥机制---
(1)同步:多个线程按照约定顺序共同的完成一个任务。
(2)互斥:保证共享资源(临界资源)(全局变量)操作的完整性,为了实现原子操作。
(3)同步与互斥的关系
    同步的基础使互斥,实现同步,一定会实现互斥。
    实现互斥不一定实现同步。
(4)线程间的同步互斥机制有3种
    1》信号量(无名信号量)
     线程的同步机制---信号量---无名信号量
     信号量代表某一类资源,其值表示系统中该资源的数量
     信号量是一个受保护的变量,只能通过四种操作来访问
     初始化信号量
     P操作(申请资源)---sem_wait
     V操作(释放资源)---sem_post
     销毁信号量
     信号量的值为非负整数
        操作步骤:
        sem_init
        #include <semaphore.h>
        int sem_init(sem_t *sem, int pshared, unsigned int value);
        函数的功能:初始化信号量
        参数:sem 要初始化的信号量
              pshared 0 线程间
                      >0 进程间
              value 信号量的初始值(资源的数量)
        返回值 :成功返回0,失败-1;
        sem_post
        int sem_post(sem_t *sem);
        函数的功能:释放信号量(资源)
        参数:sem 信号量
        返回值:成功返回0,失败-1;
        sem_wait
        int sem_wait(sem_t *sem);
        函数的功能:申请资源/信号量(消耗资源)
        参数:sem 要消耗的资源/信号量
        返回值:成功返回0,失败-1;
        sem_destroy
        int sem_destroy(sem_t *sem);
        函数的功能:销毁信号量
        参数:sem 要销毁的信号量
        返回值:成功返回0,失败-1
    2》条件变量(有名信号量)----线程间同步
        注意:使用条件变量的时候必须结合互斥锁才能实现同步。
        操作步骤:
        pthread_cond_init
        int pthread_cond_init(pthread_cond_t *restrict cond,
              const pthread_condattr_t *restrict attr);
        函数的功能:初始化条件变量
        参数:cond 要初始化的条件变量
              attr 条件变量属性信息 NULL
        返回值:成功返回0,失败-1
        pthread_cond_wait
        int pthread_cond_wait(pthread_cond_t *restrict cond,
              pthread_mutex_t *restrict mutex);
        函数的功能:等待条件的发生
        参数:cond 等待发生的条件
              mutex 互斥锁
        返回值:成功返回0,失败-1
        pthread_cond_signal
        int pthread_cond_signal(pthread_cond_t *cond);
        函数的功能:发送线程结束条件
        参数:cond 条件信息
        返回值:成功返回0,失败-1;
        pthread_cond_destroy
        int pthread_cond_init(pthread_cond_t *restrict cond,
              const pthread_condattr_t *restrict attr);
        函数的功能:销毁条件变量
        参数:cond 要销毁的条件变量
        返回值:成功返回0,失败-1
        注意:
        条件变量有一个致命的缺点,就是需要先处于等待信号发生的状态,因为信号的发生时刻是内核操作的
        程序员无法预知,如果先发送信号,再处于等待状态,就会将信号漏掉,此时就会一直阻塞等待
        解决办法:
        调用延时函数sleep();
    3》互斥锁
        互斥锁重要用来保护临界资源
        每个临界资源都由一个互斥锁来保护,任何时刻最多只能有一个线程访问该资源。
        线程必须先获取互斥锁才能访问临界资源,访问完资源后释放该资源,如果无法获得锁,线程会阻塞直到获得
        锁为止。
        操作步骤:
        pthread_mutex_init
        int pthread_mutex_init(pthread_mutex_t *restrict mutex,
              const pthread_mutexattr_t *restrict attr);
        函数的功能:初始化互斥锁
        参数:mutex 要初始化的锁
              attr 锁的属性信息,一般为NULL 默认设置
        返回值:成功返回0,失败-1;
        pthread_mutex_lock
        int pthread_mutex_lock(pthread_mutex_t *mutex);
        函数的功能:上锁
        参数:mutex 要上得锁
        返回值:成功返回0,失败-1
        pthread_mutex_unlock
        int pthread_mutex_unlock(pthread_mutex_t *mutex);
        函数的功能:解锁
        参数:mutex 要解锁的那把锁
        返回值:成功返回0,失败-1
        pthread_mutex_destroy
        函数的功能:销毁互斥锁
        参数:mutex 要销毁的锁
        返回值:成功返回0,失败-1;
        例子:定义3个全局变量count,value1,value2,在主线程当中循环对count++,count操作完事后
        分别将count的值赋值给value1,value2,子线程当中,判断如果value1!=value2时,打印count,value1,value2
        注意:
        1)锁的是对的出现的,有上锁必有解锁的过程
        2)互斥锁使用必须所有的线程都遵循这个规则,如果只有部分线程遵循这个规则,互斥锁失效
        3)当一个线程申请到这个锁的时候,其他线程阻塞等待,直到把锁释放,其他的线程才有申请锁的机会
        否则一直等待。
        4)抢占锁的顺序不一定。谁都有可能先抢占到。
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值