首页 > 其他分享 >多线程

多线程

时间:2023-04-18 10:45:48浏览次数:31  
标签:thread err void exit pthread 线程 多线程

本文按照Unix环境高级编程总结而成:

  • 线程概念
典型的进程可以看成只有一个控制线程:一个进程在某一时刻只能做一件事情。有了多个线程以后,就可以把进程设计成在某一时刻能够做多件事情,每个线程各自处理独立的任务。这种设计的好处有:
  1. 通过为每种事件类型分配单独的处理线程,可以简化处理异步事件的代码。
  2. 进程间实现内存和文件描述符的共享需要依靠操作系统提供的复杂机制,但是进程内的多个线程可以自动共享一些信息,如:代码、全局空间、堆和文件描述符。
  3. 有些问题分解可以提高整个程序的性能。单线程处理多个任务的情况下,需要把这些任务串行化。但是有多个线程的情况下,相互独立的任务可以在多个线程上交叉执行。
  4. 多线程并非只在多核处理器才能发挥作用,在单核处理器上也能发挥多线程编程模型的好处。多线程程序在执行串行化任务时不得不阻塞,由于某些线程阻塞时其他线程还可以运行,所以多线程程序在单核处理器上运行依旧可以改善性能。
  • 线程标识
就像每个进程有一个进程ID一样,每个线程也有一个线程ID。进程ID在整个系统中是唯一的,而线程ID只在所属进程唯一。 线程ID用pthread_t数据类型来表示,实现的时候可以用一个结构来代表pthread_t数据类型,所以可移植的操作系统实现不能把它作为整数处理,必须使用函数来对两个线程ID进行比较。
#include <pthread.h>
int pthread_equal(pthread_t tid1, pthread_t tid2);  若相等,返回非0数值;否则返回0.
线程可以通过调用pthread_self函数获得自身的线程ID。
pthread_t pthread_self(void);
  • 线程创建
在POSIX线程的情况下,程序开始运行时,它也是以单进程中的单个控制线程启动的,新增的线程可以通过调用pthread_create函数创建。
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t* restrict attr, void *(*start_rtn)(void*), void *restrict arg);
创建成功,返回0;否则,返回错误编号
当pthread_create成功返回时,tidp指向的内存单元被设置成线程ID,attr参数用于定制各种不同的线程属性。把它设置NULL时创建一个具有默认属性的线程。 新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg。如果需要向start_rtn函数传递多个函数,需要把这些参数放到一个结构中,然后将这个结构的地址作为arg参数传入。 线程创建时并不能保证调用线程还是新创建的线程会先运行。
#include "apue.h"
#include <pthread.h>

pthread_t ntid;

void printids(const char *s)
{
    pid_t       pid;
    pthread_t   tid;

    pid = getpid();
    tid = pthread_self();
    printf("%s pid %lu tid %lu (0x%lx)\n", s, (unsigned long)pid,
      (unsigned long)tid, (unsigned long)tid);
}

void *thr_fn(void *arg)
{
    printids("new thread: ");
    return((void *)0);
}

int main(void)
{
    int     err;

    err = pthread_create(&ntid, NULL, thr_fn, NULL);
    if (err != 0)
        err_exit(err, "can't create thread");
    printids("main thread:");
    sleep(1); //主线程休眠,如果不休眠,可能会退出,这样新线程还没运行,整个进程就终止了。
    exit(0);
}
结果:

 两个线程具有相同的进程ID,从十六进制来看,linux使用指向线程数据结构的指针作为它的线程ID。

  • 线程终止
线程有三种方式退出:
  1. 从线程的开始函数中返回,返回值是线程的退出码。
  2. 自己调用pthread_exit。
  3. 被同一进程中的其他线程取消。
void pthread_exit(void *rval_ptr);
int pthread_join(pthread_t thread, void **rval_ptr);    成功返回0;否则返回错误编号
执行pthread_join时,调用线程将一直阻塞,直到指定线程退出。如果指定线程主动退出,则rval_ptr就包含返回码;如果指定线程被取消,rual_ptr指定的内存单元就设置为PTHREAD_CANCELED。如果对线程的返回值不感兴趣,可以把rval_ptr设置为NULL。
#include "apue.h"
#include <pthread.h>

void *thr_fn1(void *arg)
{
    printf("thread 1 returning\n");
    return((void *)1);
}

void *thr_fn2(void *arg)
{
    printf("thread 2 exiting\n");
    pthread_exit((void *)2);
}

int main(void)
{
    int         err;
    pthread_t   tid1, tid2;
    void        *tret;

    err = pthread_create(&tid1, NULL, thr_fn1, NULL);
    if (err != 0)
        err_exit(err, "can't create thread 1");
    err = pthread_create(&tid2, NULL, thr_fn2, NULL);
    if (err != 0)
        err_exit(err, "can't create thread 2");
    err = pthread_join(tid1, &tret);
    if (err != 0)
        err_exit(err, "can't join with thread 1");
    printf("thread 1 exit code %ld\n", (long)tret);
    err = pthread_join(tid2, &tret);
    if (err != 0)
        err_exit(err, "can't join with thread 2");
    printf("thread 2 exit code %ld\n", (long)tret);
    exit(0);
}
结果:

可以看到当 一个线程通过调用pthread_exit退出或者从入口函数返回时,进程中的其他线程可以通过调用pthread_join获得该线程的退出状态。 pthread_exit函数的无类型指针可以传递包含复杂信息的结构的地址,但是,这个结构所使用的内存在调用者完成调用以后必须仍然是有效的。 例如,在调用线程的栈上分配了该结构,那么其他线程在使用这个结构时内存内容可能就发生了变化;又如,线程在自己的栈上分配了结构,然后把这个结构的指针传给了pthread_exit,那么调用pthread_join的线程使用该结构时,这个栈可能就被撤销或者另作他用。
#include "apue.h"
#include <pthread.h>

struct foo {
    int a, b, c, d;
};

void printfoo(const char *s, const struct foo *fp)
{
    printf("%s", s);
    printf("  structure at 0x%lx\n", (unsigned long)fp);
    printf("  foo.a = %d\n", fp->a);
    printf("  foo.b = %d\n", fp->b);
    printf("  foo.c = %d\n", fp->c);
    printf("  foo.d = %d\n", fp->d);
}

void *thr_fn1(void *arg)
{
    struct foo  foo = {1, 2, 3, 4};

    printfoo("thread 1:\n", &foo);
    pthread_exit((void *)&foo);
}

void *thr_fn2(void *arg)
{
    printf("thread 2: ID is %lu\n", (unsigned long)pthread_self());
    pthread_exit((void *)0);
}

int main(void)
{
    int         err;
    pthread_t   tid1, tid2;
    struct foo  *fp;

    err = pthread_create(&tid1, NULL, thr_fn1, NULL);
    if (err != 0)
        err_exit(err, "can't create thread 1");
    err = pthread_join(tid1, (void *)&fp);
    if (err != 0)
        err_exit(err, "can't join with thread 1");
    sleep(1);
    printf("parent starting second thread\n");
    err = pthread_create(&tid2, NULL, thr_fn2, NULL);
    if (err != 0)
        err_exit(err, "can't create thread 2");
    sleep(1);
    printfoo("parent:\n", fp);
    exit(0);
}
结果:

 可以看到当主线程方位这个结构时,结构的内容已经改变。具体如何改变据说根据内存体系结构、编译器以及线程库的实现会有所不同。 线程可以通过调用pthread_cancel来请求取消同一进程中的其他线程。
int pthread_cancel(pthread_t tid);
在默认情况下,pthread_cancel会使得由tid标识的线程的行为表现如同调用了参数为PTHREAD_CANCELED的pthread_exit函数。但是线程可以选择忽略取消或者控制如何被取消。 pthread_cancel并不等待线程的终止,它仅仅提出要求。 线程可以安排它退出时需要调用的函数,这样的函数称为线程清理处理程序。一个线程可以建立多个清理处理程序。处理程序记录在占中,它们的执行顺序与注册时相反。(待补充)
void pthread_cleanup_push(void (*rtn)(void*), void *arg);
void pthread_cleanup_pop(int execute);
默认情况下,线程的终止状态会保存直到对该线程调用pthread_join。如果线程已经被分离,线程的底层存储资源可以在线程终止时立即被回收。在线程分离后,不能用pthread_join等待它的终止状态,对分离的线程调用pthread_join会产生未定义行为。可以调用pthread_detach分离线程。
int pthread_detach(pthread_t tid);

 

标签:thread,err,void,exit,pthread,线程,多线程
From: https://www.cnblogs.com/chien/p/17328728.html

相关文章

  • C++实现多线程
    #include<iostream>#include<chrono>#include<thread>voidprintNumbers1(){for(inti=1;i<=10000;i++){std::cout<<"Thread1:"<<i<<std::endl;}}voidprintNumbers2(){for......
  • 第5章 高效的多线程日志
    日志库介绍:一个日志库大体可分为前端(frontend)和后端(backend)两部分。前端是供应用程序使用的接口(API),并生成日志消息(logmessage);后端则负责把日志消息写到目的地(destination)。在多线程程序中,前端和后端都与单线程程序无甚区别,无非是每个线程有自己的前端,整个程序共用一个后端。但难点......
  • 多线程
    一.多线程1.什么是线程要了解线程,首先需要知道进程。一个进程指的是一个正在执行的应用程序。线程对应的英文名称为“thread”,它的功能是执行应用程序中的某个具体任务,比如一段程序、一个函数等。线程和进程之间的关系,类似于工厂和工人之间的关系,进程好比是工厂,线程就如同工厂......
  • python3多线程-线程池和优先队列
    1、介绍有两种线程池方案。各线程持续存在,从任务池获取任务进行执行按照需求创建线程,每个线程只执行一个任务,结束完毕则该线程结束2、准备(1)任务池task_list任务池是用于准备各任务单元的环境,比如http爆破时的请求参数,读写文件时的路径。任务池的准备可能会占用一定时间,边准......
  • python3多线程-线程同步
    1、介绍多线程同时访问和操作同一资源,可能会造成异常。解决办法是对资源进行限制,同一时间只允许一个线程进行访问和操作。这里的资源,一般是指方法、函数或者代码块。即由多行代码组成,在逻辑上属于不可切分的操作。2、线程同步使用Thread对象的Lock和Rlock可以实现简单......
  • [计科]多进程和多线程的程序在使用上有何区别?
    区别多进程和多线程的程序在使用方式和效果上有很大的区别。多进程程序是在不同的进程之间进行协作的,每个进程都有自己独立的内存空间和系统资源。多个进程之间通过进程间通信(IPC)进行数据和信息的交换。多进程的优点在于可靠性和鲁棒性较高,一个进程出现问题不会影响到其它进程......
  • 【Linux】多线程 —— 线程概念 | 线程控制
    多线程1.线程概念1.1Linux线程与接口关系的认识1.2线程的私有&共有资源1.3线程的优缺点1.4线程的异常2.线程控制2.1pthread_create创建线程2.2pthread_join线程等待2.3线程终止的方案2.4pthrerad_detach线程分离azingneverlies正文开始@小边小边别发愁线程,是在进......
  • C#多线程学习(一) 多线程的相关概念
    C#多线程学习(一)多线程的相关概念什么是进程?当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。而一个进程又是由多个线程所组成的。什么是线程?线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码......
  • Python爬虫之多线程加快爬取速度
    之前我们学习了动态翻页我们实现了网页的动态的分页,此时我们可以爬取所有的公开信息了,经过几十个小时的不懈努力,一共获取了16万+条数据,但是软件的效率实在是有点低了,看了下获取10万条数据的时间超过了56个小时,平均每分钟才获取30条数据。注:软件运行的环境的虚拟主机,CPU:......
  • 多线程应用案例
    需求解析一个Excel中多个sheet的数据,那么此时就可以考虑使用多线程,每个线程解析一个sheet中的数据,然后等待所有的sheet数据解析完成后,再把数据入库在这个需求中,要实现主线程等待所有现场完成shee数据解析操作,第一种方案:采用join()方法publicclassMyJoinTest{publicstaticvoid......