Linux——线程同步

一.多线程
    1.了解多线程
        解决多任务实现。
        历史上Unix服务器不支持多线程
        Unix/Linux上实现多线程有两种方式:
            内核支持多线程
            使用进程的编程技巧封装进程实现多线程:轻量级多线程
        多线程的库:
            libpthread.so      -lpthread
            pthread.h                
    2.创建多线程
            2.1.代码?由回调函数实现
            2.2.线程ID?pthread_t
            2.3.运行线程?
                    pthread_create
        int pthread_create(
                pthread_t *th,//返回进程ID
                const pthread_attr_t  *attr,//线程属性,为NULL/0,使用进程的默认属性
                void*(*run)(void*),//线程代码

                void *data);//传递线程代码的数据

hehe.c

#include <stdio.h>
#include <unistd>
#include <pthread.h>
void* run(void* data)
{
	printf("我是线程\n");
} 
main()
{
	pthread_t tid;
	pthread_create(&tid,0,run,0);
	//sleep(1);
}
子进程执行不到 可以用 pthread_join()函数等待子线程结束后主线程再执行

        
        结论:
                1.主程序(主线程)结束所有子线程就结束
                        解决办法:等待子线程结束
                            sleep/pause
                        int pthread_join(
                                pthread_t tid,//等待子线程结束
                                void **re);//子线程结束的返回值
                                
                2.创建子线程后,主线程继续完成系统分配时间片。
                3.子线程结束就是线程函数返回。

                4.子线程与主线程有同等优先级别(你执行一会我执行一会).

createth.c

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
void* run(void* data)
{
	while(1)
	{
		printf("我是线程!%s\n",data);
		sched_yield();
		//return "hello";
		pthread_exit("world");		
	}
}

main()
{
	pthread_t  tid;
	char *re;
	pthread_create(&tid,0,run,"Jack");	
	pthread_join(tid,(void**)&re);
	printf("%s\n",re);
	
}


                
作业:
    写一个程序创建两个子线程                                                
                
    3.线程的基本控制
            线程的状态:
                    ready->runny->deady
                                        |
                    sleep/pause
            结束线程?                
                    内部自动结束:(建议)
                        return  返回值;(在线程函数中使用)
                        void pthread_exit(void*);(在任何线程代码中使用)                        
                    外部结束一个线程.                        
                        pthread_cancel(pthread_t);
            线程开始
                                

   demo1.c//两个线程一个负责显示随机数 一个显示时间 主线程等待键盘输入退出

#include <curses.h>
#include <pthread.h>
#include <time.h>
#include <math.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
//全局变量两个窗体
WINDOW *wtime,*wnumb;
pthread_t thnumb,thtime;
pthread_mutex_t m;

//线程1:随机数
void*runnumb(void *d)
{
	int num;
	while(1)
	{
		//循环产生7位随机数
		num=rand()%10000000;
		pthread_mutex_lock(&m);
		//显示
		mvwprintw(wnumb,1,2,"%07d",num);
		//刷新
		refresh();
		wrefresh(wnumb);
		pthread_mutex_unlock(&m);
		usleep(1);
	}
	return 0;
}
//线程2:时间
void*runtime(void*d)
{	
	time_t tt;
	struct tm *t;
	while(1)
	{
		//循环取时间
		tt=time(0);
		t=localtime(&tt);
		pthread_mutex_lock(&m);
		//显示
		mvwprintw(wtime,1,1,"%02d:%02d:%02d",
			t->tm_hour,t->tm_min,t->tm_sec);
		//刷新
		refresh();
		wrefresh(wtime);
		pthread_mutex_unlock(&m);
		usleep(1);
	}
}	

main()
{
	//初始化curses
	initscr();
	curs_set(0);
	noecho();
	keypad(stdscr,TRUE);
	wnumb=derwin(stdscr,3,11,
			(LINES-3)/2,(COLS-11)/2);
	wtime=derwin(stdscr,3,10,0,COLS-10);
	box(wnumb,0,0);
	box(wtime,0,0);
	refresh();
	wrefresh(wnumb);
	wrefresh(wtime);
	pthread_mutex_init(&m,0);//2
	//创建线程1
	pthread_create(&thnumb,0,runnumb,0);
	//创建线程2
	pthread_create(&thtime,0,runtime,0);
	//等待按键
	//结束
	getch();
	pthread_mutex_destroy(&m);//3
	delwin(wnumb);
	delwin(wtime);
	endwin();
}



               
    4.多线程的问题

            数据脏

problem.c

#include <stdio.h>
#include <pthread.h>
//1.
pthread_mutex_t m;
int a=0,b=0;
void *r1()
{
	while(1)
	{
		pthread_mutex_lock(&m);	//判定互斥量0:阻塞  若是1:置0,返回
		a++;
		b++;		
		if(a!=b)
		{
			printf("%d!=%d\n",a,b);
			a=b=0;
		}
		pthread_mutex_unlock(&m);//互斥量置1返回	
	}
}

void *r2()
{
	while(1)
	{
	
		pthread_mutex_lock(&m);	
		a++;
		b++;		
		if(a!=b)
		{
			printf("%d!=%d\n",a,b);
			a=b=0;
		}
		pthread_mutex_unlock(&m);
	}
}
main()
{
	pthread_t t1,t2;
	//2
	pthread_mutex_init(&m,0);//初始化互斥量 互斥量置1
	pthread_create(&t1,0,r1,0);
	pthread_create(&t2,0,r2,0);
	pthread_join(t1,(void**)0);//等待子进程结束
	pthread_join(t2,(void**)0);
	//4.
	pthread_mutex_destroy(&m);
}



 

    5.多线程问题的解决
            互斥锁/互斥量  mutex
            1.定义互斥量pthread_mutex_t
            2.初始化互斥量 1 pthread_mutex_init
            3.互斥量操作     置0 phtread_mutex_lock
                    判定互斥量0:阻塞
                    1:置0,返回
                    置1 pthread_mutex_unlock
                    置1返回
                    强烈要求成对使用                            
            4.释放互斥量pthread_mutex_destroy
        
        结论:
                互斥量保证锁定的代码一个线程执行,
                但不能保证必需执行完!                                            
            
            5.在lock与unlock之间,调用pthread_exit,或者在线程外部调用pthread_cancel,

               这两种行为都是遭鄙视的,其他线程被永久死锁.


            6.pthread_cleanup_push {
                pthread_cleanup_pop     }    
                这对函数作用类似于atexit,取消线程时回调某个函数
                注意:
                    这不是函数,而是宏.
                    必须成对使用

problem2.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
pthread_mutex_t m;
void handle(void *d)
{
//    printf("退出后的调用!\n");
    pthread_mutex_unlock(&m);
}

void* runodd(void *d)
{
    int i=0;
    for(i=1;;i+=2)
    {

        pthread_cleanup_push(handle,0);
        //handle函数在线程调用return,pthread_exit,pthread_cancel,
        //或者下面函数参数为1时得到回调
        pthread_mutex_lock(&m);
        printf("%d\n",i);
        pthread_cleanup_pop(1);
    }
}

void* runeven(void *d)
{
    int i=0;
    for(i=0;;i+=2)
    {
        pthread_cleanup_push(handle,0);
        pthread_mutex_lock(&m);
        printf("%d\n",i);
        pthread_cleanup_pop(1);

    }
}


main()
{
    pthread_t todd,teven;
    pthread_mutex_init(&m,0);
    pthread_create(&todd,0,runodd,0);
    pthread_create(&teven,0,runeven,0);
    sleep(5);
    pthread_cancel(todd);
    pthread_join(todd,(void**)0);
    pthread_join(teven,(void**)0);
    pthread_mutex_destroy(&m);
}
              

6.多线程的应用


二.多线程同步
    互斥量/信号/条件量/信号量/读写锁
        1.sleep与信号
            pthread_kill向指定线程发送信号
            signal注册的是进程的信号处理函数.
            
            pthread_kill+sigwait控制进程  如果线程sigwait来不及调用 不处理信号 则发给进程
            1.1.定义信号集合
            1.2.初始化信号集合
            1.3.等待信号    
            1.4.其他线程发送信号     

            1.5.清空信号集合

thsignal.c

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
pthread_t t1,t2;
sigset_t sigs;
void handle(int s)
{
	printf("信号!\n");
}
void*r1(void*d)
{	
	int s;
	while(1)
	{
		printf("线程--1\n");
		//sleep(1);
		sigwait(&sigs,&s);
		printf("接收到信号:%d!\n",s);
	}
}
void*r2(void*d)
{
	
	while(1)
	{
		printf("线程----2\n");
		sleep(2);
		pthread_kill(t1,SIGUSR1);
	}
}

main()
{
	sigemptyset(&sigs);
	sigaddset(&sigs,SIGUSR1);
	//sigfillset(&sigs);
	signal(SIGUSR1,handle);
	pthread_create(&t1,0,r1,0);
	pthread_create(&t2,0,r2,0);
	
	pthread_join(t1,(void**)0);
	pthread_join(t2,(void**)0);
	
	
}

      

            
案例:
            sigwait实际处理了信号
            如果进程没有处理信号,目标线程也没有sigwait

            ,则进程会接收信号进行默认处理


        2.条件量
            信号量类似
            2.1.定义条件量
            2.2.初始化条件量
            2.3.等待条件量    
            2.4.其他线程修改条件量
            2.5.释放条件量
            
案例:
        创建两个线程.
            一个线程等待信号
            一个线程每隔1秒发送信号

            1.使用pause+pthread_kill

signal.c  

#include <stdio.h>
#include <pthread.h>
#include <signal.h>
pthread_t t1,t2;
void handle(int s)
{
}
void *r1(void* d)
{
	while(1)
	{
		pause();	
		printf("活动!\n");
	}
}
void *r2(void* d)
{
	while(1)
	{
		sleep(1);
		pthread_kill(t1,34);
	}
}
main()
{
	signal(34,handle);
	pthread_create(&t1,0,r1,0);
	pthread_create(&t2,0,r2,0);
	pthread_join(t1,(void**)0);
	pthread_join(t2,(void**)0);
}


signal2.c

#include <stdio.h>
#include <pthread.h>
#include <signal.h>
pthread_t t1,t2;
sigset_t sigs;
void *r1(void* d)
{
	int s;
	while(1)
	{	
		sigwait(&sigs,&s);
		printf("活动!\n");	
			
	}
}
void *r2(void* d)
{
	while(1)
	{		
		sleep(1);
		pthread_kill(t1,34);
	}
}
main()
{
	sigemptyset(&sigs);
	sigaddset(&sigs,34);
	pthread_create(&t1,0,r1,0);
	pthread_create(&t2,0,r2,0);
	pthread_join(t1,(void**)0);
	pthread_join(t2,(void**)0);
}


            
        pthread_cond_*** 与sigwait都是进程同步控制
        
        pthread_cond_***稳定

        pthread_cond_***在环境下不会死锁.

signal3.c

#include <stdio.h>
#include <pthread.h>
#include <signal.h>
pthread_t t1,t2;
pthread_cond_t cond;//1.
pthread_mutex_t m;
void *r1(void* d)
{
	int s;
	while(1)
	{			
		pthread_cond_wait(&cond,&m);	
		printf("活动!\n");
	}
}
void *r2(void* d)
{
	while(1)
	{			
		pthread_cond_signal(&cond);
		pthread_cond_signal(&cond);
		pthread_cond_signal(&cond);//只输出一次 条件量不会累计
		sleep(10);
	}
}
main()
{
	pthread_mutex_init(&m,0);
	pthread_cond_init(&cond,0);//2
	pthread_create(&t1,0,r1,0);
	pthread_create(&t2,0,r2,0);
	pthread_join(t1,(void**)0);
	pthread_join(t2,(void**)0);
	pthread_cond_destroy(&cond);
	pthread_mutex_destroy(&m);
}


练习:

        使用条件量与互斥构造死锁程序.

deadlock.c

#include <stdio.h>
#include <pthread.h>

pthread_t t1,t2;
pthread_mutex_t m1,m2;
pthread_cond_t c;

void* r1(void*d)
{
    while(1)
    {
        pthread_mutex_lock(&m1);
        printf("我是等待!\n");
        pthread_cond_wait(&c,&m1);//有解锁的功能 先解锁然后决定阻塞否 之后返回原状态
        pthread_mutex_unlock(&m1);
    }
}

void* r2(void *d)
{
    while(1)
    {
        pthread_mutex_lock(&m1);
        printf("我是让你不等待!\n");
        pthread_cond_signal(&c);
        pthread_mutex_unlock(&m1);
    }
}
main()
{
    pthread_cond_init(&c,0);
    pthread_mutex_init(&m1,0);
    pthread_mutex_init(&m2,0);

    pthread_create(&t1,0,r1,0);
    pthread_create(&t2,0,r2,0);

    pthread_join(t1,0);
    pthread_join(t2,0);

    pthread_mutex_destroy(&m2);
    pthread_mutex_destroy(&m1);
    pthread_cond_destroy(&c);

}
                     

练习:
        1.写一个程序:
                两个线程写数据到文件.
                        数据格式:日期时间,线程ID\n
        要求:
                要求使用互斥, 保证数据正确.
                体会使用互斥和不使用互斥的异同.
                
        2.使用curses写一个多线程程序
                开启26个线程.每个线程控制一个字母在屏幕上掉落

                        建议每隔字母的高度随机.

demo2.c

#include <pthread.h>
#include <curses.h>
#include <math.h>
struct  AChar
{
	int x;
	int y;
	int speed;
	char a;
};
int stop=1;
pthread_t t[26];
pthread_t tid;
pthread_mutex_t m;
struct AChar  a[26];

void *run(void *d)//26个线程负责修改数据
{	
	int id;
	static idx=-1;
	idx++;
	id=idx;
	while(stop)
	{
		pthread_mutex_lock(&m);
		//改变对象的y坐标
		a[id].y+=a[id].speed;
		if(a[id].y>=LINES)
		{
			a[id].y=rand()%(LINES/4);
		}
		pthread_mutex_unlock(&m);
		sched_yield();		
		usleep(100000);
	}
}
void * update(void *d)//另写一个线程 负责显示
{
	int i=0;
	while(stop)
	{
		erase();
		//绘制屏幕上
		for(i=0;i<26;i++)
		{	
			mvaddch(a[i].y,a[i].x,a[i].a);
		}
		//刷屏
		refresh();
		usleep(10000);
	}
	
}

main()
{
	int i;
	initscr();
	curs_set(0);
	noecho();
	keypad(stdscr,TRUE);
	for(i=0;i<26;i++)
	{
		a[i].x=rand()%COLS;
		a[i].y=rand()%(LINES/4);
		a[i].speed=1+rand()%3;
		a[i].a=65+rand()%26;
	}	
	pthread_mutex_init(&m,0);	
	pthread_create(&tid,0,update,0);
	for(i=0;i<26;i++)
	{
		//随机产生字母与位置		
		pthread_create(&t[i],0,run,0);
	}	
	getch();
	stop=0;
	for(i=0;i<26;i++)
	{
		//随机产生字母与位置		
		pthread_join(t[i],(void**)0);
	}
	pthread_join(tid,(void**)0);
	pthread_mutex_destroy(&m);
	endwin();	
}


                        
        3.写一个程序:创建两个线程
                    一个线程负责找素数.
                    另外一个线程把素数保存到文件
            要求:
                    找到以后,通知另外一个线程保存,停止招素数
                    线程保存好以后通知素数查找线程继续查找.
            目的:
                    互斥与信号/条件量作用是不同.
                            
                    
                    
                        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值