多线程实现
文章目录
小知识:
修改文件夹下所有文件的权限
sudo chmod -R 777 filename
filename为要修改的文件夹名字。-R应该是表示递归修改filename文件夹下所有文件的权限。
其实整个命令的形式是
sudo chmod -(代表类型)×××(所有者)×××(组用户)×××(其他用户)
线程
• 线程有时被称为轻量级进程(LightweightProcess,LWP),是程序执行流的最小单元。
• 线程是进程中的一个实体,是被系统独立调度和分派的基本单位,
• 线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
引入线程前, 进程是资源分配的基本单位,也是调度的基本单位引入线程后, 进程是资源分配的基本单位, 线程是调度的基本单位
线程
• 允许程序执行不止一个任务的机制• 并行执行• 受操作系统异步调度, 是操作系统调度的最小单元,• 进程内的不同线程执行是同一程序的不同部分
主线程
• 线程可由进程创建,操作系统在创建进程时会创建一个主线程,程序main()函数是主线程的入口。
- 由于进程的地址空间是私有的,因此在进程间上下文切换时,系统开销比较大, 在同一个进程中创建的线程共享该进程的地址空间,
- 每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
- 线程也有就绪、阻塞和运行三种基本状态。
- Linux里同样用task_struct来描述一个线程。 线程和进程都参与统一的调度
- 多线程通过第三方的线程库来实现 New POSIX ThreadLibrary (NPTL)
多线程优点
经济实惠
• 分配的资源少、线程切换比进程切换快、维护线程的开销小
资源共享
• 线程共享它们所属进程的存储器和资源。
提高了响应速度
• 允许程序在它的一部分被阻塞或正在执行一个冗长 的操作时持续运行
提高了多处理机体系结构的利用率
• 在多CPU机器中,多线程提高了并发性
线程共享的资源
一个进程中的多个线程共享以下资源
• 可执行的指令
• 静态数据
• 进程中打开的文件描述符
• 信号处理函数
• 当前工作目录
• 用户ID
• 用户组ID
线程私有的资源
每个线程私有的资源如下
• 线程ID (TID)
• PC(程序计数器)和相关寄存器
• 堆栈
局部变量
返回地址
• 错误号(errno)
• 信号掩码和优先级
• 执行状态和属性
线程创建
NPTL线程库中提供了如下基本操作
• 创建线程
• 删除线程
• 控制线程
线程间同步和互斥机制
• 信号量
• 互斥锁
• 条件变量
pthread_create
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_tattr,
void * ( routine)(void *), void *arg);
函数参数
thread:创建的线程
attr:指定线程的属性,NULL表示使用缺省属性
routine:线程执行的函数
arg: 传递给线程执行的函数的参数
函数返回值
成功:0
出错:-1
PTHREAD_CREATE_DETACHED分离线程没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源
PTHREAD _CREATE_JOINABLE线程的默认属性是非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源
pthread_join
#include <pthread.h>
int pthread_join(pthread_t thread, void **value_ptr)
线程执行完后如果不join的话,线程的资源会一直得不到释放而导致内存泄漏!
函数参数
thread:要等待的线程
value_ptr:指针*value_ptr指向线程返回的参数
函数返回值
成功:0
出错:-1
pthread_cancel
#include <pthread.h>
int pthread_cancel(pthread_t thread)
函数参数
thread:要取消的线程
函数返回值
成功:0
出错:-1
pthread_exit
#include <pthread.h>
int pthread_exit(void *value_ptr)
函数参数
value_ptr:线程退出时返回的值
函数返回值
成功:0
出错:-1
在不终止整个进程的情况下,单个线程可以有三种方式停止其工作流并退出
1.线程从其工作函数中返回, 返回值是线程的退出码
2.线程可以被同一进程中的其他线程取消
3.线程自己调用pthread_exit
实例
#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void *thread1_func(void *arg)
{
int n;
for (n = 0; n < 30; n++) {
sleep(1);
printf("AAAAAAAAAA\n");
}
pthread_exit(NULL);
//return NULL;
}
void *thread2_func(void *arg)
{
int n;
for (n = 0; n < 30; n++) {
sleep(1);
printf("BBBBBBBBBB\n");
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid1, tid2;
if (pthread_create(&tid1, NULL, thread1_func, NULL) != 0) {
perror("main: pthread_create thread_1 failed");
return 1;
} else {
printf("main: pthread_create thread_1 succeed!\n");
}
if (pthread_create(&tid2, NULL, thread2_func, NULL) != 0) {
perror("main: pthread_create thread_2 failed");
return 1;
} else {
printf("main: pthread_create thread_2 succeed!\n");
}
if (pthread_join(tid1, NULL) != 0) {
perror("main: pthread_join thread_1 failed");
}
if (pthread_join(tid2, NULL) != 0) {
perror("main: pthread_join thread_2 failed");
}
printf("main is exiting.\n");
return 0;
}
编译
gcc thread.c -o run -lpthread
进程调度有一定随机性
whereis pthread
线程查看
ps –efL
共用同一个pid,全局变量共享,打开的文件共享。
如果LWP与PID值相同则为主线程
•UID:用户ID
• PID:process id 进程id
• PPID: parent process id 父进程id
• LWP:表示这是个线程;要么是主线程(进程),要么是线程
• NLWP: num of light weight process 轻量级进程数量,即线程数量
• STIME: start time 启动时间TIME: 占用的CPU总时间
• TTY:该进程是在哪个终端运行的
• CMD:进程的启动命令
ps -ef f
• 用树形显示进程和线程
多线程全局变量共享
注意:如果共享局部变量使用下列方法失败
#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void *thread1_func(void *arg)
{
int n;
int lfp = *(int *)arg;
for (n = 0; n < 30; n++) {
sleep(1);
printf("AAAAAAAAAA\n");
printf("A lfp = %d\n",lfp);
}
pthread_exit(NULL);
//return NULL;
}
void *thread2_func(void *arg)
{
int n;
int lfp = *(int *)arg;
for (n = 0; n < 30; n++) {
sleep(1);
printf("BBBBBBBBBB\n");
printf("A lfp = %d\n",lfp);
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid1, tid2;
int lfp = 10;
if (pthread_create(&tid1, NULL, thread1_func, &lfp) != 0) {
perror("main: pthread_create thread_1 failed");
return 1;
} else {
printf("main: pthread_create thread_1 succeed!\n");
}
lfp = 15;
if (pthread_create(&tid2, NULL, thread2_func, &lfp) != 0) {
perror("main: pthread_create thread_2 failed");
return 1;
} else {
printf("main: pthread_create thread_2 succeed!\n");
}
if (pthread_join(tid1, NULL) != 0) {
perror("main: pthread_join thread_1 failed");
}
if (pthread_join(tid2, NULL) != 0) {
perror("main: pthread_join thread_2 failed");
}
printf("main is exiting.\n");
return 0;
}
如果运行结果
在线程A中局部变量理想值应该为10,线程B中局部变量理想值才应该为15,实际中两个都为15
解决方法
专门为局部变量申请内存,或尽量不用局部变量,不要将局部变量地址传入线程
#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void *thread1_func(void *arg)
{
int n;
int lfp = *(int *)arg;
for (n = 0; n < 30; n++) {
sleep(1);
printf("AAAAAAAAAA\n");
printf("A lfp = %d\n",lfp);
}
free(arg);
pthread_exit(NULL);
//return NULL;
}
void *thread2_func(void *arg)
{
int n;
int lfp = *(int *)arg;
for (n = 0; n < 30; n++) {
sleep(1);
printf("BBBBBBBBBB\n");
printf("B lfp = %d\n",lfp);
}
free(arg);
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid1, tid2;
int lfp = 10;
int *p = malloc(sizeof(int));
*p = 10;
if (pthread_create(&tid1, NULL, thread1_func, p) != 0) {
perror("main: pthread_create thread_1 failed");
return 1;
} else {
printf("main: pthread_create thread_1 succeed!\n");
}
lfp = 15;
if (pthread_create(&tid2, NULL, thread2_func, &lfp) != 0) {
perror("main: pthread_create thread_2 failed");
return 1;
} else {
printf("main: pthread_create thread_2 succeed!\n");
}
if (pthread_join(tid1, NULL) != 0) {
perror("main: pthread_join thread_1 failed");
}
if (pthread_join(tid2, NULL) != 0) {
perror("main: pthread_join thread_2 failed");
}
printf("main is exiting.\n");
return 0;
}
多线程打开文件共享
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int fd = -1;
void *thread1_func(void *arg)
{
char buf[128] = {0};
read(fd,buf,5);
printf("thread1: buf = %s\n",buf);
pthread_exit(NULL);
//return NULL;
}
void *thread2_func(void *arg)
{
char buf[128] = {0};
read(fd,buf,5);
printf("thread2: buf = %s\n",buf);
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid1, tid2;
fd = open("./lfp",O_RDWR);
if(fd < 0){
perror("open fail\n");
}
if (pthread_create(&tid1, NULL, thread1_func, NULL) != 0) {
perror("main: pthread_create thread_1 failed");
return 1;
} else {
printf("main: pthread_create thread_1 succeed!\n");
}
lfp = 15;
if (pthread_create(&tid2, NULL, thread2_func, NULL) != 0) {
perror("main: pthread_create thread_2 failed");
return 1;
} else {
printf("main: pthread_create thread_2 succeed!\n");
}
if (pthread_join(tid1, NULL) != 0) {
perror("main: pthread_join thread_1 failed");
}
if (pthread_join(tid2, NULL) != 0) {
perror("main: pthread_join thread_2 failed");
}
printf("main is exiting.\n");
return 0;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int fd = -1;
void *thread1_func(void *arg)
{
char buf[128] = {0};
read(fd,buf,5);
printf("thread1: buf = %s\n",buf);
pthread_exit(NULL);
//return NULL;
}
void *thread2_func(void *arg)
{
fd = open("./lfp",O_RDWR);
if(fd < 0){
perror("open fail\n");
}
char buf[128] = {0};
read(fd,buf,5);
printf("thread2: buf = %s\n",buf);
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid1, tid2;
if (pthread_create(&tid1, NULL, thread1_func, NULL) != 0) {
perror("main: pthread_create thread_1 failed");
return 1;
} else {
printf("main: pthread_create thread_1 succeed!\n");
}
if (pthread_create(&tid2, NULL, thread2_func, NULL) != 0) {
perror("main: pthread_create thread_2 failed");
return 1;
} else {
printf("main: pthread_create thread_2 succeed!\n");
}
if (pthread_join(tid1, NULL) != 0) {
perror("main: pthread_join thread_1 failed");
}
if (pthread_join(tid2, NULL) != 0) {
perror("main: pthread_join thread_2 failed");
}
printf("main is exiting.\n");
return 0;
}
多线程互斥锁
线程与进程的同步互斥比较
• 多线程共享同一个进程的地址空间
• 优点:
线程间很容易进行通信
通过全局变量实现数据共享和交换
• 缺点:
多个线程同时访问共享对象时需要引入同步和互斥机制
互斥锁
互斥锁主要用来保护临界资源
• 每个临界资源都由一个互斥锁来保护,任何时刻最多只能有一个线程能访问
该资源
• 使用规则:
线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。
如果无法获得锁,线程会阻塞直到获得锁为止
mutex函数
pthread_mutex_init
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,pthread_mutexattr_t *attr)
// 初始化互斥锁
函数参数
mutex:互斥锁
attr: 互斥锁属性 // NULL表示缺省属性
函数返回值
成功:0
出错:-1
Posix Mutex API
PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略
保证了资源分配的公平性。
PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞
争。
PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样保
证当不允许多次加锁时不出现最简单情况下的死锁。PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。
pthread_mutex_unlockpthread_mutex_lock
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex)// 释放互斥锁
函数参数
mutex:互斥锁
函数返回值
成功:0
出错:-1
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex)// 申请互斥锁
函数参数
mutex:互斥锁
函数返回值
成功:0
出错:-1
举例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
//#define _LOCK_
unsigned int value1,value2, count=0;
pthread_mutex_t mutex;
void *function(void *arg);
int main(int argc, char *argv[])
{
pthread_t a_thread;
if (pthread_mutex_init(&mutex, NULL) < 0)
{
perror("fail to mutex_init");
exit(-1);
}
if (pthread_create(&a_thread, NULL, function, NULL) < 0)
{
perror("fail to pthread_create");
exit(-1);
}
while ( 1 )
{
count++;
//#ifdef _LOCK_
// pthread_mutex_lock(&mutex);
//#endif
value1 = count;
value2 = count;
//#ifdef _LOCK_
// pthread_mutex_unlock(&mutex);
//#endif
}
return 0;
}
void *function(void *arg)
{
while ( 1 )
{
#ifdef _LOCK_
pthread_mutex_lock(&mutex);
#endif
if (value1 != value2)
{
printf("count=%d , value1=%d, value2=%d\n", count, value1, value2);
usleep(100000);
}
#ifdef _LOCK_
pthread_mutex_unlock(&mutex);
#endif
}
return NULL;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
//#define _LOCK_
unsigned int value1,value2, count=0;
pthread_mutex_t mutex;
void *function(void *arg);
int main(int argc, char *argv[])
{
pthread_t a_thread;
if (pthread_mutex_init(&mutex, NULL) < 0)
{
perror("fail to mutex_init");
exit(-1);
}
if (pthread_create(&a_thread, NULL, function, NULL) < 0)
{
perror("fail to pthread_create");
exit(-1);
}
while ( 1 )
{
count++;
#ifdef _LOCK_
pthread_mutex_lock(&mutex);
#endif
value1 = count;
value2 = count;
#ifdef _LOCK_
pthread_mutex_unlock(&mutex);
#endif
}
return 0;
}
void *function(void *arg)
{
while ( 1 )
{
#ifdef _LOCK_
pthread_mutex_lock(&mutex);
#endif
if (value1 != value2)
{
printf("count=%d , value1=%d, value2=%d\n", count, value1, value2);
usleep(100000);
}
#ifdef _LOCK_
pthread_mutex_unlock(&mutex);
#endif
}
return NULL;
}
条件变量
• 条件变量是利用线程间共享的全局变量进行同步的一种机制。
• 主要包括两个动作:
一个线程等待“条件变量的条件成立”而挂起;
另一个线程使“条件成立”(给出条件成立信号)并唤醒挂起线程。
• 为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。对条件的测试是在互斥锁(互斥)的保护下进行的。
条件变量创建和注销
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond)
函数参数
cond:条件变量
函数返回值
成功:0
出错:返回错误码
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)
函数参数
cond:条件变量
attr :通常为NULL。默认值是 PTHREAD_ PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程使用。
函数返回值
成功:0
出错:返回错误码
条件变量等待
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
函数参数
cond:条件变量
mutex :互斥锁
函数返回值
成功:0
出错:返回错误码
条件变量唤醒
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cptr)
//按入队顺序唤醒其中一个
int pthread_cond_broadcast(pthread_cond_t *cptr)
唤醒所有等待线程
函数参数
cond:条件变量
函数返回值
成功:0
出错:返回错误码
实例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
pthread_mutex_t mutex;//锁定标志
pthread_cond_t cond;//唤醒条件变量
void *thread1_func(void *arg)
{
int n;
printf("enter t1\n");
pthread_mutex_lock(&mutex);//锁定
printf("t1 lock mutex\n");
for (n = 0; n < 3; n++) {
sleep(1);
printf("AAAAAAAAAA\n");
}
pthread_cond_signal(&cond);//唤醒单个线程
printf("t1 signal cond to t2\n");
pthread_mutex_unlock(&mutex);//解锁
printf("t1 unlock mutex\n");
pthread_exit(NULL);
//return NULL;
}
void *thread2_func(void *arg)
{
int n;
printf("\t\tenter t2\n");
pthread_mutex_lock(&mutex);//锁定, 阻塞前被锁上
printf("\t\tt2 lock mutex and wait\n");
pthread_cond_wait(&cond,&mutex);
printf("\t\tt2 wakeup\n");
for (n = 0; n < 3; n++) {
sleep(1);
printf("\t\tBBBBBBBBBB\n");
}
pthread_mutex_unlock(&mutex);//解锁
printf("\t\tt2 unlock mutex\n");
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t tid1, tid2;
//生成互斥锁mutex,默认属性初始化
pthread_mutex_init(&mutex,NULL);
//生成一个唤醒变量,默认属性=?同一进程内的所有线程使用
pthread_cond_init(&cond,NULL);
if (pthread_create(&tid1, NULL, thread1_func, NULL) != 0) {
perror("main: pthread_create thread_1 failed");
return 1;
} else {
printf("main: pthread_create thread_1 succeed!\n");
}
if (pthread_create(&tid2, NULL, thread2_func, NULL) != 0) {
perror("main: pthread_create thread_2 failed");
return 1;
} else {
printf("main: pthread_create thread_2 succeed!\n");
}
if (pthread_join(tid1, NULL) != 0) {
perror("main: pthread_join thread_1 failed");
}
if (pthread_join(tid2, NULL) != 0) {
perror("main: pthread_join thread_2 failed");
}
pthread_cond_destroy(&cond);//释放阻塞唤醒变量
pthread_mutex_destroy(&mutex);//释放互斥锁mutex资源
printf("main is exiting.\n");
return 0;
}