线程(thread)

线程(thread):
现代操作系统引入进程概念,为了并发(行)任务

1.进程之间的这种切换代价很高
2.通信方式的代价也很大
基本概念:

1.线程是比进程更小的资源单位,它是进程中的一个执行路线(分支)
2.线程同进程内其它线程共享地址空间(代码段、数据段、堆...)
3.线程也称为轻量级进程
线程特点:
1.创建一个线程比创建一个进程开销要小的多

2.线程间通信十分方便

3.线程也是一个动态的概念

4.在进程内创建多个线程,可以提高系统的并发处理能力

5.每个进程至少有一个线程(主线程、main线程)

6.进程是系统分配资源的最小单位,线程是任务高度的最小单位

7.标准C中不包括线程,需要额外的库才能实现线程

posix thread ->pthread


------------------
Linux下线程相关API

1.线程创建
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
功能:用于创建一个新线程
第一个参数:指针类型,用于存储新线程的ID(线程ID是用来唯一标识一个线程)
第二个参数:指定新创建线程的属性(栈空间、优先级等),一般给NULL表示采用默认属性
第三个参数:函数指针类型,新线程的执行处理函数
第四个参数:指针类型,作为第三个参数实参
返回值:成功返回0
失败返回错误编码
Compile and link with -pthread.
编译链接时要带上pthread库

man -k pthread 查看pthread库中的所有函数

2.线程标识
进程ID在整个系统中唯一的
线程ID在所所属的进程环境有效
pthread_t pthread_self(void);
功能:获得调用线程的ID

3.多线程之间的关系
调用pthread_create创建出来的新线程,叫子线程,原来的线程叫主线程,创建成功后两个纯线程独立运行,执行顺序是由操作系统调度。

在同一个进程间,主线程结束时,会导致其它所有子线程结束。(相互影响又相互独立)


4.线程等待
int pthread_join(pthread_t thread, void **retval);
第一个参数:线程ID
第二个参数:二级指针,用于获取线程的退出状态
返回值:成功返回0,失败返回错误码。
功能:一般此函数用于主线程中,等待thread指定的线程结束,如果调用成功,可以通过retval获取终止线程的返回值。
如果等待的线程没有结束,此函数将引起调用者阻塞。


5.线程的退出
void pthread_exit(void *retval);
功能:用于终止当前正在调用的线程,通过参数返回当前线程的退出状态
可以使用同一进程中其它线程调用pthread_join函数来获取退出状态


练习:
创建一个子线程,在子线程中计算圆的面积,圆的半径通过参数传递,计算成功后把结果返回给主线程,并打印出来。


4.线程的同步

回顾:
信号量集
信号量工作方式:
P操作 减1

V操作 加1

ftok()
semget()
semop()
semctl()

信号量与共享内存:
read.c ->创建
1.获取/创建信号量
2.初始化信号量
3.创建/获取共享内存
4.映射到进程地址空间
5.操作
占用信号量 P操作
read(quit退出)
释放信号量 V操作

6.删除信号量
7.解除映射
8.删除共享内存

write.c ->使用
1.创建/获取信号量
2.创建/获取共享内存
3.映射用户空间
4.操作
占用信号量 P操作
write (quit退出)
释放信号量 V操作

5.解除映射

struct message
{
char buf[256];
int num ;
};

--------------------------------
线程:
线程创建
pthread_t id;
pthread_create(&id,NULL,线程执行函数,void*arg);

线程终止
pthread_exit(void*retval);

线程等待
void *retval;
pthread_join(id,&retval);

线程取消
int pthread_cancel(pthread_t thread);
在别的线程中去取消别一个线程
被取消的线程可以设置自己的状态:
被取消的线程接收到别一个线程的取消请求,是接受还是忽略这个请求
如果接受,是立该进行终止,还是待某个函数的调用等
int pthread_setcancelstate(int state, int *oldstate);
第一个参数:设置最新状态
PTHREAD_CANCEL_ENABLE 允许被取消(默认状态)
PTHREAD_CANCEL_DISABLE 不允许被取消
第二个参数:获取之前的取消状态,不需要获取则给NULL

int pthread_setcanceltype(int type, int *oldtype);
第一个参数:设置最新取消类型
PTHREAD_CANCEL_DEFERRED 延迟取消(默认)
PTHREAD_CANCEL_ASYNCHRONOUS 立即取消
第二个参数:获取之前的取消类型,不需要获取则给NULL

线程分离:
int pthread_detach(pthread_t thread);
功能:用于标记参数指定的线程为分离状态
对于一个分离状态的线程来说,当该线程结束时,自动释放资源
使用默认值新创建的线程是非分离状态,不该线程退出时,不会主动释放资源,必须要其它线程pthread_join


------------------
线程同步问题:
在同一个进程的多线程之间共享所在进程的地址空间,如果多个线程同时访问一个共享资源,可能会导致数据的混乱,为了解决该问题就需要线程之间的协调,而线程之间的协调和通信叫线程的同步问题
gnum = 0;
gnum++;

lock
从内存把数据读到寄存器
在寄存器把数据加1
完成再把数据写回内存
unlock

1.信号量
1.定义信号量
sem_t sem;
2.初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem_init(&sem,0,1);
3.获取信号量 (信号量减1)
int sem_wait(sem_t *sem);
sem_wait(&sem);
4.访问共享资源
5.释放信号量
int sem_post(sem_t *sem);
sem_post(&sem);

6.如果不再使用,则销毁信号量
int sem_destroy(sem_t *sem);
sem_destroy(&sem);

练习:
创建两个线程

一个线程用来读文件
把写入的数据读出来

一个线程用来写文件
往文件中写入5个hello world

文件名从命令行传入


2.互斥量(锁)
3.条件变量


/*************************************************************************
> File Name: cancel.c
> Author: csgec
> Mail: longer.zhou@gmail.com
> Created Time: Fri 12 Aug 2016 11:36:35 AM CST
************************************************************************/

#include<stdio.h>
#include<pthread.h>
pthread_t id1,id2;
void *thread1(void* arg)
{
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
while(1)
{
printf("线程1正在运行! ");
sleep(1);
}
printf("线程1被取消 ");
return (void*)2;
}

void *thread2(void* arg)
{
printf("线程2在运行! ");
sleep(5);
printf("线程2请求取消线程1 ");
pthread_cancel(id1);

}

int main()
{

pthread_create(&id1,NULL,thread1,NULL);

pthread_create(&id2,NULL,thread2,NULL);
void *retval;
int r;
r = pthread_join(id1,&retval);
if(r != 0)
{
perror("join");
}
else
{
printf("pthread1 join ");
}

pthread_join(id2,NULL);

printf("thread1 retval = %d ",(int )retval);
}

/*************************************************************************
> File Name: detach.c
> Author: csgec
> Mail: longer.zhou@gmail.com
> Created Time: Fri 12 Aug 2016 02:23:07 PM CST
************************************************************************/

#include<stdio.h>
#include<pthread.h>
#include<string.h>
void *thread(void*arg)
{
int i = 5;
while(i--)
{
printf("this is thread run! ");
sleep(1);
}
}

int main()
{
pthread_t tid;
pthread_create(&tid,NULL,thread,NULL);
pthread_detach(tid);
int res;
res = pthread_join(tid,NULL);
if(res != 0)
{

fprintf(stderr,"join:%s ",strerror(res));
}

return 0;

}

/*************************************************************************
> File Name: pthread1.c
> Author: csgec
> Mail: longer.zhou@gmail.com
> Created Time: Thu 11 Aug 2016 04:16:34 PM CST
************************************************************************/

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<string.h>
void *newThread(void *arg)
{
printf("我就是那个新创建的线程 ");
printf("子线程的ID :%lu,PID是%d ",pthread_self(),getpid());
return (void*)123456;
}

int main()
{
pthread_t tid;
pthread_t mid;
int res;
void *retval;
res = pthread_create(&tid,NULL,newThread,NULL);
if(res != 0)
{
fprintf(stderr,"pthread_cread:%s",strerror(res));
exit(-1);
}

printf("主线程的ID:%lu,PID是%d ",pthread_self(),getpid());
pthread_join(tid,&retval);
printf("线程退出状态为:%d ",(int )(retval));


}

/*************************************************************************
> File Name: sem_read.c
> Author: csgec
> Mail: longer.zhou@gmail.com
> Created Time: Fri 12 Aug 2016 09:33:14 AM CST
************************************************************************/

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<string.h>
struct sembuf buf;
int sem_p(int semid)
{
buf.sem_num = 0;
buf.sem_op = -1;
buf.sem_flg = 0;
int r = semop(semid,&buf,1);
if(r == -1)
{
perror("sem_p");
return -1;
}
return 0;
}
int sem_v(int semid)
{
buf.sem_num = 0;
buf.sem_op = 1;
buf.sem_flg = 0;
int r = semop(semid,&buf,1);
if(r == -1)
{
perror("sem_v");
return -1;
}
return 0;
}
struct messge
{
int num;
char msg[256];
};
int main()
{
//1.
key_t key = ftok(".",100);
if(key < 0)
{
perror("ftok");
exit(-1);
}
int semid;
semid = semget(key,1,0666 | IPC_CREAT);
if(semid < 0)
{
perror("semget");
exit(-1);
}
//2.
semctl(semid,0,SETVAL,1);
//3.
int shmid;
shmid= shmget(key,1024,0666 | IPC_CREAT);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
//4.
void *shm_addr;
shm_addr = (char*)shmat(shmid,NULL,0);
if(shm_addr == (void*)-1)
{
perror("shmat");
exit(-1);
}
struct messge *msgd;
msgd = shm_addr;

sleep(5);
//5.
char buf[256] = {0};
while(1)
{
if(sem_p(semid))
{
exit(-1);
}
printf("进入读进程: ");
// strcpy(buf,shm_addr);

printf("消息编号是%d,读到的内容:%s ",msgd->num,msgd->msg);
printf("离开读进程! ");
if(sem_v(semid))
{
exit(-1);
}
if(strncmp(msgd->msg,"quit",4) == 0)
{
break;
}
//.
}
//6.
if(semctl(semid,0,IPC_RMID) == -1)
{
perror("semctl_rmid");
exit(-1);
}
//7.
if(shmdt(shm_addr) == -1)
{
perror("shmdt");
exit(-1);
}
//8.
if(shmctl(shmid,IPC_RMID,NULL) == -1)
{
perror("shmctl");
exit(-1);
}


return 0;
}

/*************************************************************************
> File Name: sem_read.c
> Author: csgec
> Mail: longer.zhou@gmail.com
> Created Time: Fri 12 Aug 2016 09:33:14 AM CST
************************************************************************/

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<string.h>
struct message
{
int num;
char msg[256];

};

struct sembuf buf;
int sem_p(int semid)
{
buf.sem_num = 0;
buf.sem_op = -1;
buf.sem_flg = 0;
int r = semop(semid,&buf,1);
if(r == -1)
{
perror("sem_p");
return -1;
}
return 0;
}
int sem_v(int semid)
{
buf.sem_num = 0;
buf.sem_op = 1;
buf.sem_flg = 0;
int r = semop(semid,&buf,1);
if(r == -1)
{
perror("sem_v");
return -1;
}
return 0;
}

int main()
{
//1.
key_t key = ftok(".",100);
if(key < 0)
{
perror("ftok");
exit(-1);
}
int semid;
semid = semget(key,1,0666 | IPC_CREAT);
if(semid < 0)
{
perror("semget");
exit(-1);
}
//2.
// semctl(semid,0,SETVAL,1);//做为非创建者,不需要初始化,直接用就可
//3.
int shmid;
shmid= shmget(key,1024,0666 | IPC_CREAT);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
//4.
void *shm_addr;
shm_addr = (char*)shmat(shmid,NULL,0);
if(shm_addr == (void*)-1)
{
perror("shmat");
exit(-1);
}
struct message *msgd = shm_addr;
msgd->num = 0;
//5.
char buf[256] = {0};
while(1)
{
if(sem_p(semid))
{
exit(-1);
}
printf("进入写进程: ");
fgets(buf,sizeof(buf),stdin);
msgd->num++;
strcpy(msgd->msg,buf);
printf("离开写进程! ");

if(sem_v(semid))
{
exit(-1);
}
if(strncmp(buf,"quit",4) == 0)
{
break;
}
//.
}
//7.
if(shmdt(shm_addr) == -1)
{
perror("shmdt");
exit(-1);
}

return 0;
}

/*************************************************************************
> File Name: test.c
> Author: csgec
> Mail: longer.zhou@gmail.com
> Created Time: Fri 12 Aug 2016 02:30:31 PM CST
************************************************************************/

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

#include <semaphore.h>


sem_t sem;
void *thread(void*arg)
{
sem_wait(&sem);
int num = (int)arg;
int i;
for(i = 0; i < 5; i++)
{
printf("这是线程%d ",num);
sleep(1);
}
sem_post(&sem);

}
int main()
{
sem_init(&sem,0,1);
pthread_t tid1,tid2;
printf("ni ma ");
pthread_create(&tid1,NULL,thread,(void*)1);

pthread_create(&tid2,NULL,thread,(void*)2);

pthread_join(tid1,NULL);
printf("ni er ju ");
pthread_join(tid2,NULL);

printf("ni da ye ");

sem_destroy(&sem);

return 0;
}

原文地址:https://www.cnblogs.com/liudehao/p/5765234.html