zoukankan      html  css  js  c++  java
  • 8、多线程小结(1)

    1、在Linux中,线程一般被认为是轻量级的进程

        Linux 创建进程所使用的函数是fork()或者vfork()。而对线程的创建和管理Linux可以使用POSIX的线程库pthreads提供的APIs

    2、使用fork()创建进程和使用POSIX线程库差别:

        使用fork() 创建进程的特点:

    代价昂贵,通常子进程需要拷贝父进程的整个上下文,比如数据等。

    进程间的通信方式比较复杂,比如使用管道、消息、共享内存等方法。

    操作系统在实现进程间的切换比线程切换更费时

        使用POSIX pthreads库创建线程的特点:

    线程可使用存在于进程中的资源,因此创建进程比创建线程更

    线程间的通信方式更容易,比如通过进程中的变量,可以让多个线程共享数据。

    操作系统对线程的切换比对进程的切换更容易和快速。

    3、线程由内核自动调度,每个线程都有它自己的线程上下文(thread context),包括一个惟一的整数线程IDThread ID,TID),栈,栈指针,程序计数器,通用目的寄存器和条件码。每个线程和其他线程一起共享进程上下文的剩余部分,包括整个用户的虚拟地址空间,它是由只读文本(代码),读/写数据,堆以及所有的共享库代码和数据区域组成的,还有,线程也共享同样的打开文件的集合。

    4、常见IPC有:管道,FIFO,共享存储器,信号。

    5、线程间无需特别的手段进行通信,因为线程间可以共享数据结构,注意的是线程间需要做好同步。linux的消息属于IPC,也就是进程间通信,线程用不上。

        pthread_self() 的返回值就是当前线程的标志。

    示例代码

    View Code
    #include <stdio.h>
    #include
    <stdlib.h>
    #include
    <unistd.h>
    #include
    <pthread.h>

    #define THREAD_NUMBER 2
    int retval_hello1= 2, retval_hello2 = 3;

    void* hello1(void *arg)
    {
    char *hello_str = (char *)arg;
    sleep(
    1);
    printf(
    "%s\n", hello_str);
    pthread_exit(
    &retval_hello1);
    }

    void* hello2(void *arg)
    {
    char *hello_str = (char *)arg;
    sleep(
    2);
    printf(
    "%s\n", hello_str);
    pthread_exit(
    &retval_hello2);
    }

    int main(int argc, char *argv[])
    {
    int i;
    int ret_val;
    int *retval_hello[2];

    pthread_t pt[THREAD_NUMBER];

    const char *arg[THREAD_NUMBER];
    arg[
    0] = "hello world from thread1";
    arg[
    1] = "hello world from thread2";
    printf(
    "Begin to create threads...\n");

    ret_val
    = pthread_create(&pt[0], NULL, hello1, (void *)arg[0]);
    if (ret_val != 0 )
    {
    printf(
    "pthread_create error!\n");
    exit(
    1);
    }

    ret_val
    = pthread_create(&pt[1], NULL, hello2, (void *)arg[1]);
    if (ret_val != 0 )
    {
    printf(
    "pthread_create error!\n");
    exit(
    1);
    }
    printf(
    "Now, the main thread returns.\n");
    printf(
    "Begin to wait for threads...\n");

    for(i = 0; i < THREAD_NUMBER; i++)
    {
    ret_val
    = pthread_join(pt[i], (void **)&retval_hello[i]);
    if (ret_val != 0)
    {
    printf(
    "pthread_join error!\n");
    exit(
    1);
    }
    else
    {
    printf(
    "return value is %d\n", *retval_hello[i]);
    }
    }
    return 0;
    }

    5、属性设置

    属性名

    意义

    detachstate

    选择被创建的线程是处于可加入的状态还是分离状态。可加入状态值是PTHREAD_CREATE_JOINABLE;分离状态值是PTHREAD_CREATE_DETACHED。缺省状态值是PTHREAD_CREATE_JOINABLEpthread_attr_setdetachstate() 可设置线程为加入或者分离状态; pthread_attr_getdetachstate() 可以获得当前线程是否是加入的或者是分离的状态。

    schedpolicy

    为被创建的线程选择调度策略。被创建的线程的状态可以是SCHED_OTHER(一般的,非实时调度)SCHED_RR (实时,轮转调度) 或者SCHED_FIFO(实时,先进先出调度)。缺省值是SCHED_OTHER。实时调度SCHED_RR SCHED_FIFO 只能用于有超级用户权限的进程使用。 pthread_attr_setschedpolicy() 和  pthread_attr_getschedpolicy() 函数可以设置和获得线程的调度属性。

    schedparam

    为被创建的线程选择调度参数。这里的调度参数指的是线程的调度优先级。缺省优先级是 0 。这个属性对于 SCHED_OTHER 是不重要的;它只对SCHED_RR SCHED_FIFO 两个和实时调度相关的调度方式有效。 pthread_attr_setschedparam() pthread_attr_getschedparam() 两个函数可以分别对线程的优先级进行设置和获取。

    inheritsched

    选择对新创建的线程的调度策略和调度参数是否被schedpolicy schedparam 属性决定(这时的值是PTHREAD_EXPLICIT_SCHED)或者是通过父线程继承而得到的(这时的值是PTHREAD_INHERIT_SCHED)。缺省的值是PTHREAD_EXPLICIT_SCHED

    scope

    为选择被创建的线程调度竞争范围。缺省值是PTHREAD_SCOPE_SYSTEM,表示线程和系统的所有的其他运行在CPU上的进程争夺CPU 资源。如果是PTHREAD_SCOPE_PROCESS,表示调度的竞争只发生在运行于同一进程空间的线程之间,线程的优先级只在同一进程空间的线程之间有效,和其他进程无关。

    6、线程互斥锁的特性

    原子性。对mutex的加锁和解锁操作是原子的,一个线程进行 mutex 操作的过程中,其他线程不能对同一个 mutex 进行其他操作。

    单一性。拥有mutex的线程除非释放mutex,否则其他线程不能拥有此mutex

    非忙等待。等待mutex的线程处于等待状态,直到要等待的mutex处于未加锁状态,这时操作系统负责唤醒等待此mutex的线程。

    7、条件变量

        条件标量是线程的同步设备。在线程间使用条件变量可以使得一个线程在执行过程中,因满足某个条件而发出信号通知另一个线程;而另一个线程可以处于挂起状态,等待某个条件的满足后,才继续执行。

        条件变量必须和mutex一起使用来避免竞争情况。

    8、一个线程可以通过向另个线程发送“请求”来结束另一个线程的执行。

    POSIX pthreads库中关于撤销操作的函数有:   

    int pthread_setcancelstate(int state, int *oldstate);

    int pthread_setcanceltype(int type, int *oldtype);

    void pthread_testcancel(void);

        在撤销线程的时候,可以编写程序让线程进一步进行所谓的“清理”工作,比如已经拥有了某个 mutex,在清理例程中可以释放这个 mutex;如果动态分配了内存,那么可以在清理例程中释放动态分配的内存。

    9、信号

    #include <signal.h>

    int pthread_sigmask(int how, const sigset_t  *newmask,  sigset_t  *oldmask);

        用来改变或者设置线程的信号屏蔽(signal mask)newmask 用来执行新的信号屏蔽,设置新信号屏蔽以前的信号屏蔽被存放到 oldmask 指向的位置。

    int pthread_kill(pthread_t thread, int signo);

    可以向其他线程发送信号。

    int sigwait(const sigset_t *set, int *sig);

    挂起调用sigwait() 的线程,直到收到第一个参数 set 指向的信号集中指定的信号,且等待到信号被存放到第二个参数  sig 指向的位置。

    10、常用函数整理

    1) 创建

    int pthread_create(

    pthread_t* thread,          //返回指向线程标识符的指针                

    pthread_attr* attr,          //设置线程属性            

    void *(*func)(void*),       //新线程将启动的函数的地址

    void* arg                        //传递给新线程启动函数的参数

    );

        //函数执行成功返回0,失败返回错误码。执行成功后,新线程将从设定的函数处开始执行,原线程则继续执行。

    2)终止

    void pthread_exit(void* ret)

           //终止调用此函数的线程,并返回一个指向某对象的指针(ret),可通过pthread_join来获取此返回值

           //注意,不能返回指向局部变量的指针。

  • 相关阅读:
    Html禁止粘贴 复制 剪切
    表单标签
    自构BeanHandler(用BeansUtils)
    spring配置中引入properties
    How Subcontracting Cockpit ME2ON creates SD delivery?
    cascadia code一款很好看的微软字体
    How condition value calculated in sap
    Code in SAP query
    SO Pricing not updated for partial billing items
    Javascript learning
  • 原文地址:https://www.cnblogs.com/mydomain/p/2141512.html
Copyright © 2011-2022 走看看