zoukankan      html  css  js  c++  java
  • Chapter11 线程

    1. void 和void* 的使用

        void 意思是无类型,void* 意思是无类型指针,可以指向所有数据类型。

        (1) void 主要用于对函数参数和返回值的限定,加上void 限定则表明该函数不接受参数或者无返回值(因为在C语言中,即使函数声明时无参数,在函数调用时传进参数是不会报错的,即使函数没有指定返回类型,函数会默认返回int类型,所以加上void 限定是十分有必要的)

        (2) void* 则多用于函数参数(或返回值)可以是任意类型的情况下,因为void* 可以接受任意类型而无需强制装换,但将其转换为具体类型则需要强制装换,这有点类似于C++中的继承关系。

    2. 线程创建与线程标识

        线程ID 用pthread_t 数据结构来表示,在不同的操作系统中表示方法不同,所以可移植的操作系统实现不能把它作为整数处理,因此必须使用 pthread_equal() (函数原型:int pthread_equal(pthread_t tid1, pthread_t tid2);)函数来对像个线程ID 进行比较。调用 pthread_self 函数可以获取自身线程ID。

        在传统Unix进程模型中,每个进程只有一个线程。在POSIX线程的情况下, 当程序开始运行时,也是以单进程中的单个控制线程启动的,并可以通过pthread_create函数创建新线程。(函数原型: int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rth)(void*), void *restrict arg);) 其中tidp为 ID,attr为订制线程属性,start_rth 是线程开始运行的函数地址,arg是该函数的参数(若由多个参数,则使用结构体)

        《APUE》 程序清单 11-1 打印线程ID:

     1 #include "apue.h"
     2 #include <pthread.h>
     3 
     4 pthread_t ntid;
     5 
     6 void printids(const char* s) {
     7     pid_t     pid;
     8     pthread_t tid;
     9 
    10     pid = getpid();
    11     tid = pthread_self();
    12     printf("%s pid %u tid %u (0x%x)
    ", s, (unsigned int)pid,
    13         (unsigned int)tid, (unsigned int)tid);
    14 }
    15 
    16 void* thr_fn(void* arg) {
    17     printids("new thread: ");
    18     return ((void*)0);
    19 }
    20 
    21 int main()
    22 {
    23     int err;
    24     err = pthread_create(&ntid, NULL, thr_fn, NULL);
    25     if (err != 0)
    26         err_quit("can't create thread: %s
    ", strerror(err));
    27     printids("main thread: ");
    28 
    29     sleep(1);
    30     exit(0);
    31 }

        程序运行结果:

         main thread:  pid 20961 tid 4253615936 (0xfd890740)
         new thread:  pid 20961 tid 4245337856 (0xfd0ab700)
       进程ID 相同, 线程ID 不同

    3. 线程终止

        终止线程有三种方式,在不终止整个进程的情况下终止它的控制流(注意进程中任一线程调用了exit, _Exit 或者_exit ,那么整个进程就会终止)

    • 线程只是从启动历程中返回,返回值是线程的退出码 (return)
    • 线程可以被同一进程中的其他线程取消。(int pthread_cancel(phread_ tid);)
    • 线程调用pthread_exit. (函数原型: void pthread_exit(void *rval_ptr);  )

        有时程序要了保证在主进程结束前线程运行结束,会在主进程中调用sleep。也可以使用pthread_join (函数原型: int pthread_join(pthread_t thread, void** rval_ptr);  )

        函数,调用该函数时,调用线程会一直阻塞,直到指定的线程调用pthread_exit,从启动例程中返回或者别取消。另外通过pthread_join函数还可以访问到 pthread_exit()

        参数中的rval_ptr 指针。

    4. 线程清理处理程序(thread cleanup handler)

        线程可以安排它退出时需要调用的函数,这些处理程序记录在栈中,可以使用pthread_cleanup_push() 函数注册多个清理处理程序。

        在下面三种情况下,已经注册的清理处理程序将会被调用

    •     调用pthread_exit时 (注意return时不会调用)
    •     相应取消请求时
    •     用非零参数调用pthread_cleanup_pop 时 (与pthread_cleanup_push相对应)

    5. 线程同步

        在变量修改时间多于一个存储器(非原子性)访问周期的处理器结构中,且当存储器读与存储器写这两个周期交叉时,潜在的不一致性就会出现。如果修改操作是原子操作,那么就不存在竞争。

    *互斥量:可以通过pthread的虎吃接口保护数据,确保同一时间只有一个线程访问数据。

        pthread_mutex_t   互斥变量数据类型

        pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t * restrict attr) 对互斥量进行初始化,attr = NULL 则使用默认属性;也可使用常量     

                    PTHREAD_MUTEX_INITIALIZER 进行初始化

        pthread_mutex_destroy(pthread_mutex_t* mutex); 动态分配的互斥量(如malloc),释放内存前要调用该函数

        pthread_mutex_lock(phtread_mutex_t* mutex); 对互斥量进行加锁

        pthread_mutex_unlock(phtread_mutex_t* mutex); 对互斥量解锁

        pthread_mutex_trylock(pthread_mutex_t* mutex) 尝试加锁,未阻塞返回0, 否则失败返回EBUSY

    * 避免死锁

        死锁可以通过小心地控制互斥量加锁的顺序来避免。

        以下代码中,每个foo 结构都由 一个f_lock 互斥量来保护该数据结构的引用次数,另外还有一个hashlock 来维护一个用于跟踪foo数据结构的散列列表,这里为了避免死锁需要小心地维护f_lock 和hashlock 的加锁顺序。

        《APUE》程序清单 11-6 使用两个互斥量

    #include <stdlib.h>
    #include <pthread.h>
    
    #define NHASH 29
    #define HASH(fp) (((unsigned long)fp)%NHASH)
    
    struct foo* fh[NHASH];
    
    pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER;
    
    struct foo {
        int             f_count;
        pthread_mutex_t f_lock;
        struct foo*     f_next;
        int             f_id;
    };
    
    struct foo* foo_alloc(void)
    {
        struct foo* fp;
        int    idx;
    
        if ((fp = malloc(sizeof(struct foo))) != NULL) {
            fp->f_count = 1;
            if (pthread_mutex_init(&fp->f_lock, NULL) != NULL) {
                free(fp);
                return (NULL);
            }
            idx = HASH(fp);
            pthread_mutex_lock(&hashlock);
            fp->f_next = fh[idx];
            fh[idx] = fp;
            pthread_mutex_lock(&fp->f_lock);
            pthread_mutex_unlock(&hashlock);
    
            pthread_mutex_unlock(&fp->f_lock);
        }
        return (fp);
    }
    
    void foo_hold(struct foo* fp)
    {
        pthread_mutex_lock(&fp->f_lock);
        fp->f_count++;
        pthread_mutex_unlock(&fp->f_lock);
    }
    
    struct foo* foo_find(int id)
    {
        struct foo* fp;
        int    idx;
    
        idx = HASH(fp);
        pthread_mutex_lock(&hashlock);
        for (fp = fh[idx]; fp != NULL; fp = fp->f_next) {
            if (fp->f_id == id) {
                foo_hold(fp);
                break;
            }
        }
        pthread_mutex_unlock(&hashlock);
        return (fp);
    }
    
    void foo_rele(struct foo* fp)
    {
        struct foo* tfp;
        int    idx;
    
        pthread_mutex_lock(&fp->f_lock);
        if (fp->f_count == 1) {
            pthread_mutex_unlock(&fp->f_lock);
            pthread_mutex_lock(&hashlock);
            pthread_mutex_lock(&fp->f_lock);
    
            /* need to recheck the condition */
            if (fp->f_count != 1) {
                fp->f_count--;
                pthread_mutex_unlock(&fp->f_lock);
                pthread_mutex_unlock(&hashlock);
                return;
            }
    
            /* remove from list */
            idx = HASH(fp);
            tfp = fh[idx];
            if (tfp == fp) {
                fh[idx] = fp->f_next;
            } else {
                while (tfp != fp)
                    tfp = tfp->f_next;
                tfp->f_next = fp->f_next;
            }
    
            pthread_mutex_unlock(&hashlock);
            pthread_mutex_unlock(&fp->f_lock);
            pthread_mutex_destory(&fp->f_lock);
        } else {
            fp->f_count--;
            pthread_mutex_unlock(&fp->f_lock);
        }
    }

        程序11-6 可以只通过hashlock 来保护引用计数来使事情大大简化,而结构互斥量f_lock 则可用来保护foo结构中的其他任何东西。

  • 相关阅读:
    sql 执行动态语句
    Cookie/Session机制详解
    .NET简谈事务、分布式事务处理
    .NET(C#)中不同级别的安全透明代码对类型的影响
    C#开发微信门户及应用(1)开始使用微信接口
    WIN7管理工具配置ODBC数据源系统DSN中无Oracle,Sybase驱动的解决方法
    题解 smoj 2806 【建筑物】
    题解 luogu P2568 GCD
    题解 luogu P1251 【餐巾计划问题】
    0377组合总和IV Marathon
  • 原文地址:https://www.cnblogs.com/Patrickcxt/p/3918881.html
Copyright © 2011-2022 走看看