........................................................................线程.................................................................
线程:计算机中独立运行的最小单位,在运行时占用很少的系统资源。
Linux操作系统支持多线程操作,在一个进程内生成许多个线程,即一个进程可以拥有一至多个线程。
1.创建线程 :pthread_create()
int pthread_create(pthread_t *thread,pthread_attr_t *attr,void* (*start_routine)(),void *arg);
thread:当线程创建成功时,用来返回创建的线程ID
attr:用于指定线程的书写,NULL表示使用默认属性。
start_routine:函数指针,指向线程创建后要调用的函数。
arg:该参数指向传递给线程函数的参数。
线程创建成功时,pthread_create函数返回0,若不为0,则说明创建线程失败。
相关系统函数:
pthread_t pthread_self():获取本线程的线程ID
int pthread_once(pthread_once_t *once_control,void(*init_routine)()):用来保证init_routine线程函数在进程中执行一次。
请看下面例子:
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<errno.h>
#include<stdlib.h>
#if 1
int* thread()
{
pthread_t newtid;
printf("this is a thread,thread ID = %u\n",newtid);
return NULL;
}
int main()
{
pthread_t tid;
printf("main thread,ID is %u\n",pthread_self()); //打印主线程的ID
if(pthread_create(&tid,NULL,(void*)thread,NULL)!=0)
{
printf("thread creation failed\n");
exit(1);
}
sleep(1);
exit(0);
}
#endif
运行结果如下:
程序先打印出主线程的ID,然后打印新创建的线程的ID
也可以同时创建多个线程,分别打印线程信息。
#if 1
//pthread_self()--获取自身线程ID号
void* thread_fun(void *arg)
{
printf("this id fun,tid = %x.pid = %d\n",pthread_self(),getpid());
printf("fun ends\n");
}
void* thread_fun1(void *arg)
{
printf("this id fun1,tid = %x.pid = %d\n",pthread_self(),getpid());
printf("fun1 ends\n");
}
int main()
{
pthread_t tid1,tid2;
int res = pthread_create(&tid1,NULL,thread_fun,NULL);
if(res != 0)
{
perror("pthread_create");
exit(1);
}
else
{
printf("tid1 = %x,pid = %d\n",tid1,getpid());
sleep(1); //加入睡眠函数 会等待子线程执行
}
res = pthread_create(&tid2,NULL,thread_fun1,NULL);
if(res != 0)
{
perror("pthread_create tid2");
exit(1);
}
else
{
printf("tid2 = %x.pid = %d\n",tid2,getpid());
sleep(1);
}
int i;
for(i=0;i<10;i++)
{
printf("This is Main.\n");
}
return 0;
}
#endif
2.pthread_join()---等待一个线程的结束。
3.pthread_exit()---使线程退出。
pthread_join用来等待一个线程的结束。相当于进程中的wait(),
需要注意的是:一个线程仅允许一个进程使用pthread_join()等待他的终止,并且被等待的线程应该处于可join状态。
例子如下:
/*
pthread_join()--等待一个线程的结束或者终止一个线程 类似于进程中的wait()函数。
函数原型 int pthread_join(pthread_t thread, void **retval);
当前进程ID号
注:一个线程仅允许一个线程使用pthread_join()等待他的终止
pthread_exit() --结束线程
*/
#if 1
int fun(int a,int b)
{
return a + b;
}
typedef int(*pFun)(int ,int);
void* thread_fun(void *arg)
{
pFun pfun = (pFun)arg;
printf("value = %d\n",pfun(10,20));
char *exit_code = "I am Child Thread,finsh.\n";
pthread_exit(exit_code);
}
int main()
{
pthread_t tid;
int count = 5;
int res = pthread_create(&tid,NULL,thread_fun,fun);
if(res != 0)
{
perror("pthread_create");
exit(1);
}
int i;
for(i=0;i<10;++i)
{
printf("This is Main.\n");
}
char *retval;
pthread_join(tid,(void**)&retval); 会阻塞主线程,等待子线程thread_fun结束
printf("exit code = %s\n",retval);
return 0;
}
#endif
![](https://img-blog.csdn.net/20170901161653363?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl5Zl9fODg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
前面我们说过一个进程可以创建多个线程,下面我们看一个例子。
#if 1
//一个进程创建多个线程的方式--添加循环
#define MAX_THREAD_SIZE 10
void* thread_fun(void *arg)
{
int index=*(int*)arg;
printf("[%d] thread start up.\n",index);
pthread_exit(NULL);
}
int main()
{
pthread_t tid[MAX_THREAD_SIZE];
int i;
for(i=0;i<MAX_THREAD_SIZE;++i)
{
pthread_create(&tid[i],NULL,thread_fun,&i);
sleep(1);
}
for(i=0;i<MAX_THREAD_SIZE;++i)
{
pthread_join(tid[i],NULL);
}
return 0;
}
#endif
运行结果如下:
上述代码采用循环的方式创建了10个线程,依次循环创建多个线程。
**********************线程同步****************************
线程最大的特点就是资源的共享性,但是资源共享中的同步问题是多线程编程的难点。
因此,有几种方式可以处理线程间的同步问题,分别是:
1.互斥量
2.信号量
3.条件变量
4.读写锁
###########互斥量###########
..........互斥量...............
线程对共享资源访问要想达到同步,即 两个线程只能有一个运行 -通过互斥量实现
互斥量通过锁机制来实现线程间的同步,在同一时刻只允许一个线程执行一个关键部分。
互斥量的 两种初始化方式;
1. int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);//动态初始化
2. pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //静态赋值
初始化完成后,就可以给互斥锁加锁。
互斥量的两个状态:
pthread_mutex_lock(pthread_mutex_t *mutex); 加锁
加锁时,如果mutex已经被锁住,当前尝试加锁的线程就会阻塞,直到互斥锁被其他线程释放。当函数返回时,说明互斥锁已经被当前线程成功加锁。
pthread_mutex_unlock(pthread_mutex_t *mutex);解锁
解锁时,满足两个条件:一是互斥锁必须处于加锁状态,二是调用本函数的线程必须是给互斥锁加锁的线程。解锁后如果还有其他线程在等待互斥锁,等待队列中的第一个线程将获得互斥锁
当一个互斥锁使用完毕后,必须进行清除,释放它所占用的资源。
注意:清除锁时要求当前处于开放状态,若锁处于锁定状态,函数返回EBUSY。
摧毁函数:int pthread_mutex_destroy(pthread_mutex_t *mutex)
...................................条件变量...................
(同互斥量相似)
1.初始化函数
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
2.等待函数: int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
此函数释放由mutex指向的互斥锁,同时使当前线程关于cond指向的条件变量阻塞,直到条件被信号唤醒
两大功能:阻塞的同时还可以解锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
3.摧毁函数: int pthread_cond_destroy(pthread_cond_t *cond);
只有在没有线程等待该条件变量时才能清除这个环境变量,否则返回EBUSY
#if 1
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //初始化方式1:静态赋值法
//pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex;
pthread_cond_t cond;
void* thread1(void *arg)
{
while(1)
{
printf("thread1 is running!\n");
pthread_mutex_lock(&mutex);
printf("This is thread1.wait....\n");
pthread_cond_wait(&cond,&mutex);
//无wait函数时,fun1执行完走fun2,
printf("thread1 wake up.\n");
pthread_mutex_unlock(&mutex); //在同一个进程中的线程,如果加锁后没有解//锁,则其他线程则无法获得该锁
sleep(3);
}
}
void* thread2(void *arg)
{
while(1)
{
printf("thread2 is running!\n");
pthread_mutex_lock(&mutex);
printf("This is thread2.wait……\n");
pthread_cond_wait(&cond,&mutex);
printf("thread2 wake up\n");
pthread_mutex_unlock(&mutex);
sleep(1);
}
}
int main()
{
pthread_t tid1,tid2;
printf("condition variable study!\n");
pthread_mutex_init(&mutex, NULL);//通过初始化函数初始化互斥锁
pthread_cond_init(&mutex,NULL); //初始化条件变量
pthread_create(&tid1,NULL,thread1,NULL);
sleep(1); //使thread1先运行
pthread_create(&tid2,NULL,thread2,NULL);
do{
//激活一个等待条件成立的过程,存在多个等待线程时,按照入队顺序激活。
pthread_cond_signal(&cond);
}while(1);
pthread_exit(0);
return 0;
}
#endif
即互斥量扮演着一个保护者的角色,保证条件变量能被正确的修改。
pthread_cond_wait(),调用pthread_cond_wait()前必须由本线程加锁(pthread_mutex_lock()),而在更新条件等待队列之前,mutex保持锁定状态,并在线程挂起前解锁。
写一个小程序来复习学习的线程操作以及线程同步
创建两个线程,分别执行计数功能,互斥操作分别打印奇数和偶数
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<errno.h>
#include<stdlib.h>
#include<string.h> //memset
#if 1
#define MAX_COUNT 10
int count = 1;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;
void* fun1(void *arg)
{
sleep(3);
while(count <= MAX_COUNT)
{
pthread_mutex_lock(&mutex);
if(count % 2 == 1) //奇数时执行,偶数时阻塞去执行另一个线程
{
printf("fun1:%d\n",count);
count++;
sleep(1);
pthread_cond_signal(&cond);//发送信号,唤醒等待的进程
}
else
{
pthread_cond_wait(&cond1, &mutex);
printf("This is oushu\n");
}
pthread_mutex_unlock(&mutex);
}
}
void* fun2(void *arg)
{
while(count <= MAX_COUNT)
{
pthread_mutex_lock(&mutex);
if(count%2 == 0)//偶数时执行,奇数时阻塞去执行另一个线程
{
printf("fun2:%d\n",count);
count++;
sleep(1);
pthread_cond_signal(&cond1);
}
else
{
pthread_cond_wait(&cond, &mutex);
printf("This is jishu\n");
}
pthread_mutex_unlock(&mutex);
}
}
int main()
{
pthread_t tid1,tid2;
pthread_create(&tid1,NULL,fun1,NULL);
pthread_create(&tid2,NULL,fun2,NULL);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}
#endif
运行结果如下: