关于线程

来源:互联网 发布:柳州广电网络年费多少 编辑:程序博客网 时间:2024/06/08 15:55


........................................................................线程.................................................................

线程计算机中独立运行的最小单位,在运行时占用很少的系统资源。

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 1int* 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());  //打印主线程的IDif(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 1int 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

前面我们说过一个进程可以创建多个线程,下面我们看一个例子。

#if 1//一个进程创建多个线程的方式--添加循环#define MAX_THREAD_SIZE 10void* 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


从运行结果来看,thread1和thread2通过条件变量同步运行,在线程函数thread1中可以看到条件变量使用时要配合互斥锁使用,这样可以防止多个线程同时请求。
即互斥量扮演着一个保护者的角色,保证条件变量能被正确的修改。

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 10int 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

运行结果如下: