zoukankan      html  css  js  c++  java
  • C语言多线程操作

    C语言多线程

    多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程

    • 基于进程的多任务处理是程序的并发执行。
    • 基于线程的多任务处理是同一程序的片段的并发执行。

    多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程,每个线程定义了一个单独的执行路径。

    本教程假设您使用的是 Linux 操作系统,我们要使用 POSIX 编写多线程 C++ 程序。POSIX Threads 或 Pthreads 提供的 API 可在多种类 Unix POSIX 系统上可用,比如 FreeBSD、NetBSD、GNU/Linux、Mac OS X 和 Solaris。

    创建线程

    下面的程序,我们可以用它来创建一个POSIX 线程:

    #include <pthread.h>
    pthread_create (thread, attr, start_routine, arg)

    在这里,pthread_create 创建一个新的线程,并让它可执行。下面是关于参数的说明:

    参数描述
    thread 指向线程标识符指针。
    attr 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。
    start_routine 线程运行函数起始地址,一旦线程被创建就会执行。
    arg 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。

    创建线程成功时,函数返回 0,若返回值不为 0 则说明创建线程失败。

    终止线程

    使用下面的程序,我们可以用它来终止一个 POSIX 线程:

    #include <pthread.h>
    pthread_exit (status)

    在这里,pthread_exit 用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。

    如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。

    连接和分离线程

    我们可以使用以下两个函数来连接或分离线程:

    pthread_join (threadid, status) 
    pthread_detach (threadid)

    pthread_join() 子程序阻碍调用程序,直到指定的 threadid 线程终止为止。当创建一个线程时,它的某个属性会定义它是否是可连接的(joinable)或可分离的(detached)。只有创建时定义为可连接的线程才可以被连接。如果线程创建时被定义为可分离的,则它永远也不能被连。pthread_join() 函数来等待线程的完成。

    注意

    pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a, 在使用pthread_create创建线程时,在编译中要加-lpthread参数:

      gcc createThread.c -lpthread -o createThread.o
      ./createThread.

    Test 1 无参数传递的线程并发编程实例

    // 基于线程的并发编程
    // Test_1 createThread
    #include <stdio.h>
    #include <pthread.h>
    /*
     * pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,
     * 在使用pthread_create创建线程时,在编译中要加-lpthread参数:
     * gcc createThread.c -lpthread -o createThread.o
     * ./createThread.o
     * 加上上面两句以后编译成功,并输出结果
     * */
    #define NUM_Threads 5
    
    // 线程的运行函数
    void *PrintHello(void *arg)
    {
        printf("Hello,World of Thread in C!
    ");
        return 0;
    }
    
    int main()
    {
        int i;
        int ret;
        // 定义线程的id变量,多个变量使用数组
        pthread_t tids[NUM_Threads];
    
        for (i=0; i<NUM_Threads; i++)
        {
            // 参数依次是: 创建的线程id,线程参数,调用的函数,传入的函数参数
           ret = pthread_create(&tids[i], NULL, PrintHello, NULL);
           if (ret != 0)
          {
              printf("pthread_create error: error_code = 
    ");
          }
        }
        // 等各个线程推出后,进程才结束
        pthread_exit(NULL);
    
        return 0;
    }
    
    /*
     * 在CLion(Ubuntu)中输出结果为
    Hello,World of Thread in C!
    Hello,World of Thread in C!
    Hello,World of Thread in C!
    Hello,World of Thread in C!
    Hello,World of Thread in C!
     * */

    Test 2 简单参数传递的线程并发编程实例

    // 基于线程的并发编程,向线程传递参数1
    // Test_2_createThread
    #include <stdio.h>
    #include <pthread.h>
    #include <stdlib.h>
    #define NUM_Threads 5
    
    // 线程的运行函数
    void *PrintHelloId(void *threadid)
    {
        // 对传入的参数进行强制类型转换,由无类型指针变为整形指针,然后再读取
        int tid = *((int *)threadid);
        printf("Hello,World, Thread %d
    ",tid);
        return 0;
    }
    
    int main()
    {
        pthread_t pthreads[NUM_Threads];
        int i, rc;
        // 用数组存储i的数值
        int indexes[NUM_Threads];
    
        for (i=0; i<NUM_Threads; i++)
        {
            printf("main() : 创建线程 %d 
    ",i);
            indexes[i] = i; // 保存i的数值
            // 在indexes传入参数的时候必须转换为无类型指针
            rc = pthread_create(&pthreads[i], NULL, PrintHelloId, (void *)&indexes[i]);
            if (0 != rc)
            {
                printf("Error: 无法创建线程!
    ");
                exit(-1);
            }
        }
    
        pthread_exit(NULL);
        return 0;
    }
    
    /*
     * 在CLion(Ubuntu)中输出结果是
    main() : 创建线程 0
    main() : 创建线程 1
    Hello,World, Thread 0
    main() : 创建线程 2
    Hello,World, Thread 1
    main() : 创建线程 3
    Hello,World, Thread 2
    main() : 创建线程 4
    Hello,World, Thread 3
    Hello,World, Thread 4
     * */

    Test 3 结构体参数传递的线程并发编程实例

    // 基于线程的并发编程,向线程传递参数2(传递结构体)
    // Test_3_createThread
    #include <stdio.h>
    #include <pthread.h>
    #include <stdlib.h>
    #define NUM_Threads 5
    
    typedef struct thread_data{
        int threadid;
        char message;
    }THDATA,*PTHDATA;
    
    void * PrintHello(void * pthreadid)
    {
        PTHDATA tid = (PTHDATA)pthreadid;
    
        printf("This is Pthread : %d ;info : %c 
    ",tid->threadid, tid->message);
    
        return 0;
    }
    
    int main(void)
    {
        pthread_t Pthread[NUM_Threads];
        THDATA index[NUM_Threads];
        int i, ret;
    
        for (i = 0; i < NUM_Threads; i++)
        {
            printf("main() : 创建线程 %d 
    ",i);
            index[i].threadid = i;
            index[i].message = 'A'+i%10;
            ret = pthread_create(&Pthread[i], NULL, PrintHello, (void *)&index[i]);
            if (0 != ret)
            {
                printf("Error: 创建线程失败!
    ");
                exit(-1);
            }
        }
        pthread_exit(NULL);
        return 0;
    }
    
    /*
     * 在CLion(Ubuntu)中输出结果是
    main() : 创建线程 0
    main() : 创建线程 1
    This is Pthread : 0 ;info : A
    main() : 创建线程 2
    main() : 创建线程 3
    This is Pthread : 2 ;info : C
    main() : 创建线程 4
    This is Pthread : 3 ;info : D
    This is Pthread : 4 ;info : E
    This is Pthread : 1 ;info : B
     * */

    Test 4 线程的连接编程实例

    // 基于线程的并发编程,连接或分离线程
    // Test_4_createThread
    // 2019年10月27日14:45:11 尚未完全搞明白
    #include <stdio.h>
    #include <pthread.h>
    #include <stdlib.h>
    
    #define NUM_Pthread 5
    
    void *PrintHello(void * pthreadid)
    {
        int tid = *((int *)pthreadid);
        printf("Sleeping in thread %d ,...exiting 
    ",tid);
        return 0;
    }
    
    int main(void)
    {
        int i, ret;
        pthread_t Pthread[NUM_Pthread];
        pthread_attr_t attr; // 定义线程属性
        void * status;
        int index[NUM_Pthread];
    
        // 初始化并设置线程为可连接
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    
        for (i=0; i<NUM_Pthread; i++)
        {
            printf("main() : 创建线程 %d 
    ",i);
            index[i] = i;
            ret = pthread_create(&Pthread[i], NULL, PrintHello, (void *)&index[i]);
        }
    
        // 删除属性,并等待其他线程
        pthread_attr_destroy(&attr);
        for (i=0; i<NUM_Pthread; i++)
        {
            ret = pthread_join(Pthread[i], status);
            if (0 != ret)
            {
                printf("Error: unable to join,%d
    ",ret);
                exit(-1);
            }
            printf("main(): complete thread id : %d",i);
            printf(" exiting with status : %p
    ",status);
        }
    
        printf("main() : program exiting.
    ");
        pthread_exit(NULL);
    
        return 0;
    }

    信号量机制

    Test 5 信号量同步进行写入

    // 用信号量进行同步
    #include <stdio.h>
    #include <pthread.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <semaphore.h>
    
    #define Len 100       // 设置输入内容长度
    
    sem_t bin_sem;
    char work_area[Len]; // 存放输入内容
    
    void *Thread_func(void *arg)
    {
        // 等待信号量有大于0的值然后退出
        sem_wait(&bin_sem);
        while (0 != strncmp("end", work_area, 3))
        {
            printf("Input %ld characters
    ", strlen(work_area)-1);
        }
        return 0;
    }
    
    int main(void)
    {
        int res;    // 存放命令的返回值
        pthread_t Pthread; // 创建线程
        void *thread_result; // 存放线程处理结果
    
        // 初始化信号量,并设置初始值为0
        res = sem_init(&bin_sem, 0, 0);
        if (0 != res)
        {
            perror("Semaphore initialization failes");
            exit(EXIT_FAILURE);
        }
        // 创建新线程 0
        res = pthread_create(&Pthread, NULL, Thread_func, NULL);
        if (0 != res)
        {
            perror("Thread creation failed");
            exit(EXIT_FAILURE);
        }
        printf("Enter 'end' to finish
    ");
        // 当工作区内不是以end开头的字符串,则继续输入
        while (0 != strncmp("end", work_area, 3))
        {
            // 以标准输入获取输入到工作区内
            fgets(work_area, Len, stdin);
            sem_post(&bin_sem);  // 信号量+1
        }
        printf("
     Waiting for thread to finish...
    ");
        // 等待线程结束
        res = pthread_join(Pthread, &thread_result);
        if (0 != res)
        {
            perror("Thread join failed");
            exit(EXIT_FAILURE);
        }
        printf("Thread joined
    ");
        sem_destroy(&bin_sem);  // 销毁信号量
        exit(EXIT_SUCCESS);
    
        return 0;
    }

    Test 6 互斥信号量实现对临界资源操作

    // 用互斥信号量进行同步
    #include <stdio.h>
    #include <pthread.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define Len 3 // 自增计算次数
    #define NUM_Pthread 5 // 设置线程的长度
    
    int count = 1; // 在数据段共享资源,多个进程抢占临界资源
    // 对于临界资源,应该添加互斥锁
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    void *Thread_func(void *threadid)
    {
        int tid = *((int *)threadid);
        int i, val;
        printf("Pthread ID : %d 
    ",tid);
    
        for (i=0; i<NUM_Pthread; i++)
        {
            pthread_mutex_lock(&mutex);
            val = count;
            printf("val = %d 
    ",val++);
            count = val;
            pthread_mutex_unlock(&mutex);
        }
    
        return 0;
    }
    
    int main(void)
    {
        int res;    // 存放命令的返回值
        int i;
        pthread_t Pthread[NUM_Pthread]; // 创建线程
        int index[NUM_Pthread];
    
        for (i=0; i<NUM_Pthread; i++)
        {
            index[i] = i;
            // 创建线程
           res = pthread_create(&Pthread[i], NULL, Thread_func, (void *)&index[i]);
           if (0 != res)
           {
               printf("Error: 创建线程失败!
    ");
               exit(-1);
           }
        }
    
        for (i=0; i<NUM_Pthread; i++)
        {
            // 汇合线程
            pthread_join(Pthread[i], NULL);
        }
        printf("count = %d
    ",count);
        pthread_exit(NULL);
        return 0;
    }
    
    // 在运行此程序无互斥锁时,我们不仅得到错误的答案,而且每次得到的答案都不相同
    // 分析
    // 当多个对等线程在一个处理器上并发运行时,机器指令以某种顺序完成,每个并发执行定义了线程中指令的某种顺序

    参考博文:

    本人计算机小白一枚,对编程有浓厚兴趣,在此贴出自己的计算机学习历程,还有很多不足,望多多指教! 读书后发现好多的内容与具体专业有偏差,没来得及完成,虽然“有时间我就会做...”是人生最大的谎言,但有时间我会继续搞定未完成的内容,有始有终,兴趣使然!
  • 相关阅读:
    Linux内存管理 【转】
    Linux内核源码分析--内核启动之(2)Image内核启动(汇编部分)(Linux-3.0 ARMv7) 【转】
    Linux内核源码分析--内核启动之(1)zImage自解压过程(Linux-3.0 ARMv7) 【转】
    Linux的软中断处理实现 【转】
    u_boot移植之内存基础知识DDR【转】
    linux内存管理-内核用户空间 【转】
    底板芯片组与内存映射(Motherboard Chipsets and the Memory Map) 【转】
    深入理解C语言的函数调用过程 【转】
    JAVA学习第三十六课(经常使用对象API)— Set集合:HashSet集合演示
    二叉链表的建立和遍历 完整的前,中,后和层序建立和遍历
  • 原文地址:https://www.cnblogs.com/Robin5/p/11748016.html
Copyright © 2011-2022 走看看