LinuxC----线程总结

一.线程和进程的关系

为什么要引入线程?

CPU的执行速度过快,只有寄存器仅仅可以追上,RAM和别的设备更是难以追上,而进程和线程只不过是对应的CPU时间段的描述.
进程是cpu资源分配的最小单位,线程是cpu调度的最小单位

线程和进程的联系有

(1)进程是资源的分配和调度的一个独立单元,线程是CPU调度的基本单元
(2)同一个进程中包括多个线程,所有的线程共享进程的地址空间,可以在执行过程中节省时间
(3)系统调度时,切换的速度十分快,主要是因为进程地址空间独立而进程的线程独享地址空间
(5)线程不能独立存在,必须依附于进程
(6)进程所使用的内存地址可以限定使用通过信号来控制

线程的优点:

(1)可以提高应用程序的响应速度
(2)可以提高多处理器的效率
(3)可以改善程序的结构

线程可以独立有自己的数据信息

(1)通过使用互斥锁来保存
(2)线程号
(3)堆栈
(4)信号掩码
(5)优先级
(6)线程私有的存储空间

二.创建线程

1.pthread_create - 线程创建
头文件:

#include < pthread.h > int pthread_create
(pthread_t * thread , const pthread_attr_t * attr , void *(* start_routine )(void*), void * arg );

 #include < pthread.h > int pthread_create(pthread_t * thread , const pthread_attr_t * attr , void *(* start_routine )(void*), void * arg ); 

pthread_create()函数用于在进程内创建具有由attr指定的属性的新线程。 如果attr为NULL,则使用默认属性。 如果稍后修改attr指定的属性,则不会影响线程的属性。 成功完成后, pthread_create()将创建的线程的ID存储在线程引用的位置。


创建线程执行start_routine , arg作为其唯一参数。 如果start_routine返回,则效果就像是使用start_routine的返回值作为退出状态对pthread_exit()进行隐式调用。 请注意,最初调用main()的线程与此不同。 当它从main()返回时,效果就好像使用main()的返回值作为退出状态对exit()进行了隐式调用。


新线程的信号状态初始化如下:

信号掩码继承自创建线程。
待处理新线程的信号集为空。
如果pthread_create()失败,则不会创建新线程,并且未定义线程引用的位置的内容。

返回值
如果成功,则pthread_create()函数返回零。 否则,返回错误号以指示错误。
错误
如果出现以下情况, pthread_create()函数将失败:
[EAGAIN]
系统缺少创建另一个线程所需的资源,或者会超出系统对进程PTHREAD_THREADS_MAX中线程总数的限制。
[EINVAL]
attr指定的值无效。
[EPERM]
调用者没有适当的权限来设置所需的调度参数或调度策略。
pthread_create()函数不会返回[EINTR]的错误代码。


创建线程的其他系统的函数

函数说明
pthread_t pthreadself(void)获取本线程的线程ID
int pthread_equal(pthread_t thread1,pthread_t thread2)判断两个进程ID是不是指向同一进程
int pthread_once(pthread_once_t * once_control,void(*init_routine)(void)用来保证线程函数在进程中只执行一次

如何正确的创建线程?

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>//创建线程所需的头文件

int * thread(void * arg)//线程所调用的函数
{
     pthread_t newthid;

     newthid = pthread_self();
     printf("this is a new thread , thread  ID = %u \n",newthid);
     return NULL;
}

int main(void)
{
    pthread_t thid;
    printf("main thread , ID is %u \n",pthread_self());///打印主线程的ID
    if(pthread_create(&thid,NULL,(void *)thread,NULL)!=0)
    {
        exit(1);
    }
    sleep(1);//让主线程等待子线程结束,不然无法打印相应的值
    return 0;
}

注:在这里sleep是必要的,如果不是用sleep的话,主线程也就是main函数会直接退出,主线程的退出引起了进程的结束,从而导致了子线程的结束,看不到第二次的printf的值

三.线程属性

1.pthread_attr_t详解

typedef struct
{
   int detachstatte;
   int schedpolicy;
   struct sched_param schedparam;
   int inheritsched;
   int scope;
   size_t guardsize;
   int stackaddr_set;
   void * stackaddr;
   size_t stacksize;
}pthread_attr_t;
  • detachstata 表示新创建的线程是不是与进程中的其他的线程脱离同步,它的缺省属性是PTHREAD_CREATE_JOINABLE
    可以使用pthread_detach()来设置,设置pthread_detach()之后线程运行结束后会自动释放系统状态
  • schedpolicy表示新线程的调度策略,主要有SCHED_other(正常,非实时),(SCHED_RR)表示轮转法,SCHED_FIFO(实时,先入先出)
  • schedparam,一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,并可以在运行时通过pthread_setschedparam()函数来改变,缺省为0。
  • inheritsched,有两种值可供选择:PTHREAD_EXPLICIT_SCHED和PTHREAD_INHERIT_SCHED,前者表示新线程使用显式指定调度策略和调度参数(即attr中的值),而后者表示继承调用者线程的值。缺省为PTHREAD_EXPLICIT_SCHED。
  • scope,表示线程间竞争CPU的范围,也就是说线程优先级的有效范围。POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,前者表示与系统中所有线程一起竞争CPU时间,后者表示仅与同进程中的线程竞争CPU。目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。
  • guardsize: 警戒堆栈的大小
  • stackaddr_set: 堆栈地址集
  • stackaddr : 堆栈的地址
  • stacksize :堆栈大小

线程终止:
Linux下有三种线程终止的方式:
(1)通过return 从线程函数返回
(2) 通过调用函数pthread_exit()让线程退出
(3)甲线程可以让乙线程使用pthread_cancel()函数从而让它退出
pthread_exit在pthread.h中声明

 #include < pthread.h>
 void pthread_exit(void * retval)

在主线程中,如果从main函数返回或者是调用了exit函数退出主线程,整个进程将终止,另外一种情况是如果主线程调用了pthread_t函数,仅仅是主函数消亡,进程并不会结束,直到所有的线程结束,进程才会结束


那么线程终止之后的资源释放的问题该如何处理?

系统为我们提供了一对函数,pthread_cleanup_push(),pthread_cleanup_pop()用于自动释放资源
他们是以宏的方式进行提供的,也就是说明了
在push和pop之间不能使用return 函数,不然将不会清理完毕
cleanup_push ()把东西压入栈中,pop把栈中的东西弹出

#  define pthread_cleanup_push(routine, arg) \
  do {                                        \
    __pthread_cleanup_class __clframe (routine, arg)

/* Remove a cleanup handler installed by the matching pthread_cleanup_push.
   If EXECUTE is non-zero, the handler function is called. */
#  define pthread_cleanup_pop(execute) \
    __clframe.__setdoit (execute);                        \
  } while (0)

线程的同步问题:
一般来说,进程中的各个线程的运行是相互独立的,线程的终结并不会相互通知,也不会影响其他线程,终结的线程所占用的资源不会随着线程的结束而归还系统,有些资源得不到释放
和进程中类似的是进程中有wait函数,线程中有pthread_join函数

int pthread_join (pthread_t __th, void **__thread_return)

需要注意的是一个线程只允许一个线程使用pthread_join()等待它的终止,而且被等待的线程应该处于可join状态,即非DETACHED状态,DETACHED状态是指堆某个线程执行了pthread_deatch()函数所处的状态,处于DETACHED状态的线程无法由pthread_join()同步
一个可”join”的线程所占用的内存仅仅当由线程执行了pthread_join之后


四.私有数据及线程同步

线程的私有数据是通过一键多值来实行的,一个键对应多个值

#include<pthread.h>
 int pthread_key_create (pthread_key_t *__key,
                   void (*__destr_function) (void *))
     __THROW __nonnull ((1));

/* Destroy KEY.  */
 int pthread_key_delete (pthread_key_t __key) __THROW;

/* Return current value of the thread-specific data slot identified by KEY.  */
 void *pthread_getspecific (pthread_key_t __key) __THROW;

/* Store POINTER in the thread-specific data slot identified by KEY. */
 int pthread_setspecific (pthread_key_t __key,
                const void *__pointer) __THROW ;
  • pthread_key_create: 从LInux中的TSD中分配一项,将值给予key供以后使用,key表示的是指向键值的指针,第二个参数为一个函数指针,如果指针不为空,在线程退出之后将以key所关联的数据为参数调用destr_function(),释放缓存区
  • pthread_setspecific:通过将pointer于key相关联,指定一个新数据,会同时释放旧数据
  • pthread_getspecific: 通过函数的得到于key相关联的数据
  • pthread_key_delete :函数用来删除一个键,删除后,键所占用的内存会被释放,但是线程数据的释放必须在释放键之前完成
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>

pthread_key_t key;

void * thread2(void * arg)
{
    int tsd=5;
    printf("thread %d is running\n",pthread_self());//获取pid
    pthread_setspecific(key,(int *)tsd);//给予了一个特殊的建位
    printf("thread %d returns %d\n",pthread_self(),pthread_getspecific(key));//getspecific函数获取私有数据的键值
}
void * thread1(void * arg)
{
    int tsd=0;
    pthread_t thid2;

    printf("thread %d is running\n",pthread_self());//获取pid
    pthread_setspecific(key,(int *)tsd);//给予特殊的建位这个建位为0
    pthread_create (&thid2,NULL,thread2,NULL);
    sleep(1);
    printf("thread %d returns %d\n",pthread_self(),pthread_getspecific(key));
}

int main(void)
{
   pthread_t thid1;
   printf("main thread begins runnning \n");
   pthread_key_create (&key,NULL);//创建一个建值,第二个参数如果不为空的话,线程退出之后
   pthread_create (&thid1,NULL,thread1,NULL);//创建一个线程,同时调用thread1线程函数
   sleep(3);
   pthread_key_delete (key);
   printf("main thread exit\n");
   return 0;
}

2.互斥锁的使用

函数功能
pthread_mutex_init 函数初始化一个互斥锁
pthread_mutex_destory 函数注销一个互斥锁
pthread_mutex_lock 函数加锁,如果不成功,阻塞等待
pthread_mutex_unlock 函数解锁
pthread_mutex_trylock 函数测试加锁,如果不成功就返回

互斥锁的初始化
“静态赋值法: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
通过函数来进行初始化操作


int pthread_mutex_intit (pthread_mutex_t * mutex, const pthread_mutexattr_t *mutexattr);
加锁的同时,如果mutex已经被锁主,当前尝试加锁的线程就会阻塞,直到互斥锁被其他线程释放,当pthread_mutex_lock返回的时候,说明,互斥锁已经被当前线程成功的加锁,pthread_mutex_trylock函数不同,会返回一个EBUSY

int pthread_mutex_unlock(pthread_mutex_t * mutex);

解锁必须满足两个条件,一是互斥锁必须处于加锁状态,二是线程必须是给互斥锁加锁的线程

当一个互斥锁使用完毕之后,必须进行清除,使用函数pthread-mutex_destroy函数

int pthread_mutex_destory(pthread_mutex_t * mutex);

清除的时候要求当前必须处于开放状态,如果锁定状态,返回EBUSY

3条件变量
条件变量是利用线程间共享的全局变量进行同步的一种机制
主要包括两个动作:一是等待使用资源的线程等待”条件变量被设置真”,另一个是一个线程在使用完资源后,”设置条件为真”

条件变量可以让我们睡眠到某种状态的出现
pthread_cond_wait与pthread_mutex配合使用


函数功能
pthread-cond_init 函数初始化变量
pthread_cond_wait 函数基于条件变量阻塞,无条件等待
pthread-cond_timedwait 函数阻塞直到指定时间发生
pthread_cond_singal 函数接触特定线程的阻塞,存在多个等待线程按入队顺序激活其中一个

条件变量的初始化方式也有两种
“静态赋值法: pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
通过函数来进行初始化操作

int pthread_cond_ntit (pthread_cond_t * cond, pthread_condattr_t * cond_attr);


等待条件成立的函数有两种:
int pthread_cond_wait (pthread_cond_t *__restrict __cond,
pthread_mutex_t *__restrict __mutex)

int pthread_cond_timedwait (pthread_cond_t *__restrict __cond,
pthread_mutex_t *__restrict __mutex,
const struct timespec *__restrict __abstime)


pthread_cond_wait中的mutex必须为适应锁或者互斥锁
pthread_cont_wait返回自动对互斥锁重新进行加锁
销毁条件变量释放空间,必须在没有条件变量的等待的基础之上

pthread_cond_wait函数释放由mutex指向的互斥锁,同时让当前进程关于cond指向的条件变量阻塞,直到条件被信号唤醒

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>
#include<string.h>

pthread_mutex_t mutex;//互斥锁
pthread_cond_t cond;//操作的条件变量

void * thread1(void * arg)
{
    pthread_cleanup_push (pthread_mutex_unlock,&mutex);//用于自动释放资源,pthread_clean_up_push()的调用点和pthread_clean_pop()的调用点

    while(1)
    {
        printf("thread1 is running\n");
        pthread_mutex_lock (&mutex);//建立互斥锁,
        //无论那种类型的锁,使用pthread_mutex_lock加锁时候,如果mutex被锁住,目前尝试加锁的线程就会阻塞,
        //其中一个必须等待解索,等待队列中的第一个线程将会获得互斥锁
        pthread_cond_wait (&cond,&mutex);//释放mutex指向的互斥锁,同时让当前线程关于cond指向的条件变量阻塞,直到条件被信号唤醒
        //线程改变条件变量的值,条件变量会获得一个信号,使得等待条件变量的线程退出阻塞状态
        printf ("thread1 applied the condition\n");
        pthread_mutex_unlock(&mutex);//将互斥锁解锁
        sleep(4);
    }
    pthread_cleanup_pop(0);
}

void *thread2(void * arg)
{
       while(1)
       {
           printf("thread2 is running\n");
           pthread_mutex_lock(&mutex);//实行互斥锁
           pthread_cond_wait(&cond,&mutex);//防止多个线程请求
           //基于条件等待,释放互斥锁
           printf("thread2 applied the condition\n");
           pthread_mutex_unlock(&mutex);//
           sleep(1);
       }
}

int main(void)
{
     pthread_t tid1,tid2;

     printf("condition variable study!\n");
     pthread_mutex_init(&mutex,NULL);
     pthread_cond_init(&cond,NULL);
     pthread_create(&tid1,NULL,(void *)thread1,NULL);
     pthread_create(&tid2,NULL,(void *)thread2,NULL);

    do{
        pthread_cond_signal(&cond);
    }while(1);
    sleep(10);
    pthread_exit(0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值