线程与线程控制

原文链接:http://www.orlion.ga/1250/

一、线程

同一进程的多个线程共享同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在个线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,个线程还共享一下进程资源和环境:

    • 文件描述符
    • 每种信号的处理方式(SIG_IGN、SIG_DFL或者自定义的信号处理函数)
    • 当前工作目录
    • 用户id和组id

但有些资源使每个线程各有一份的:

    • 线程id
    • 上下文,包括各种寄存器的值、程序计数器和栈指针
    • 栈空间
    • errno变量
    • 信号屏蔽字
    • 调度优先级

二、线程控制

1、创建线程

#include <pthread.h>

int pthread_create(pthread_t *restrict thread, const pthread_attr_t * restrict attr, void *(*start_routine)(void *), void * restrict arg);

返回值:成功返回0,失败返回错误号。其他的系统函数都是成功返回0,失败返回-1,而错误号保存在全局变量errno中,而pthread库的函数都是通过返回值返回错误号,虽然每个线程也都有一个errno,但这是为了兼容其他函数接口而提供的,pthread库本身并不使用它,通过返回值返回错误码更加清晰。

在一个线程中调用pthread_create()创建线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。start_routine函数接收一个参数,是通过ptherad_create的arg参数传递给它的,该参数的类型为void *,这个指针按什么类型解释由调用者自己定义。start_routine的返回值类型也是void *,这个指针的含义同样由调用者自己定义。start_routine返回时,这个线程就退出了,其他线程可以调用pthread_join得到start_routine的返回值,类似于父进程调用wait()得到子进程的退出状态。

pthread_create成功返回后,新创建的线程的id被填写到thread参数所指向的内存单元。进程id的类型是pid_t,每个进程的id在整个系统中是唯一的,调用getpid()可以获得当前进程的id,是一个正整数值。线程id的类型是thread_t,它只在当前进程中保证是唯一的,在不同的系统中thread_t这个类型有不同的实现,他可能是一个整数值,也可能是一个结构体,也可能是一个地址,所以不能简单的当成整数用printf打印,调用pthread_self()可以获得当前线程的id。

attr参数表示线程属性。例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
pthread_t ntid;
void printids(const char *s)
{
        pid_t      pid;
        pthread_t  tid;
        pid = getpid();
        tid = pthread_self();
        printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int)pid,
               (unsigned int)tid, (unsigned int)tid);
}
void *thr_fn(void *arg)
{
        printids(arg);
        return NULL;
}
int main(void)
{
        int err;
        err = pthread_create(&ntid, NULL, thr_fn, "new thread: ");
        if (err != 0) {
                fprintf(stderr, "can‘t create thread: %s\n", 
strerror(err));
                exit(1);
        }
        printids("main thread:");
        sleep(1);
        return 0;
}

编译(gcc编译时要加上选项 -lpthread)运行结果:

可知在linux上,thread_t类型是一个地址值,属于同一进程的多个线程调用getpid()可以得到相同的进程号,而调用pthread_self()得到的线程号各不相同

由于pthread_create的错误码不保存在errno中,因此不能直接用perror()打印错误信息,可以先用strerror()把错误码转成错误信息再打印

如果任意一个线程调用了exit或_exit,则整个进程的所有线程都终止,由于从main函数return也相当于调用exit,为了防止新创建的线程还没有得到执行就终止,我们在main函数return之前延时1秒,但是即使主线程等待1秒钟,内核也不一定会调度新创建的线程执行。

2、终止线程

只终止线程而不终止进程的方法有三种

    • 从线程函数return。主线程return相当于调用了exit。
    • 一个线程可以调用pthread_cancel终止同一进程中的另一线程
    • 线程可以调用pthread_exit终止自己。

用pthread_cancel终止一个线程分同步和异步两种情况,比较复杂。

#include <pthread.h>

void pthread_exit(void *value_ptr);

value_ptr是void *类型,和线程函数返回值的用法一样,其他线程可以调用pthread_join获得这个指针。

pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其他线程得到这个返回指针时线程函数已经退出了。

#include <pthread.h>

int pthread_join(pthread_t thread, void **value_ptr);

返回值:成功返回0,失败返回错误号

调用该函数的线程将挂起等待,直到id为thread的线程终止。thread线程一不同的方法终止,通过pthread_join得到的终止状态是不同的,总结如下:

      • 如果thread线程通过return返回,value_ptr所指向的单元里存放的是thread线程函数的返回值。
      • 如果thread线程被别的线程调用pthread_cancel异常终止掉,value_ptr所指向的单元里存放的是常数PTHREAD_CANCELED。
      • 如果thread线程是自己调用pthread_exit终止的,value_ptr所指向的单元存放的是传给pthread_exit的参数。

如果对thread线程的终止状态不感兴趣,可以传NULL给value_ptr参数。

例:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.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);
}
void *thr_fn3(void *arg)
{
        while(1) {
                printf("thread 3 writing\n");
                sleep(1);
        }
}
int main(void)
{
        pthread_t   tid;
        void        *tret;
        pthread_create(&tid, NULL, thr_fn1, NULL);
        pthread_join(tid, &tret);
        printf("thread 1 exit code %d\n", (int)tret);
        pthread_create(&tid, NULL, thr_fn2, NULL);
        pthread_join(tid, &tret);
        printf("thread 2 exit code %d\n", (int)tret);
        pthread_create(&tid, NULL, thr_fn3, NULL);
        sleep(3);
        pthread_cancel(tid);
        pthread_join(tid, &tret);
        printf("thread 3 exit code %d\n", (int)tret);
        return 0;
}

结果:

可见在Linux的pthread库中常数PTHREAD_CANCELED的值是-1.可以在头文件pthread.h中找到它的定义:

#define PTHREAD_CANCELED ((void *)-1)
  • 一般情况下,流程终止后,其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它所占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL。对一个尚未detach的线程调用pthread_join或pthread_detach都可以把该线程置为detach状态,也就是说不能对同一线程调用两次pthread_join,或者如果已经对一个线程调用pthread_detach就不能再调用pthread_join了。
#include <pthread.h>

int pthread_detach(pthread_t tid);
  • 返回值:成功返回0,失败返回错误号。
时间: 2024-10-10 20:30:51

线程与线程控制的相关文章

JAVA 并发编程之线程中断的控制

今天,拿一个简单例子来说明线程中断的控制. 场景:在特定的目录,寻找特定的文件,如果找到,则10秒线程中断.如果没找到,直接抛出InterruptedException异常,并在run()方法捕获处理这个异常. 1.创建一个类名为FileSearch类,并且实现Runnable接口. public class FileSearch implements Runnable{} 2.声明两个私有属性,一个是将要查找的文件的目录,一个是文件名称.并且实现构造器,初始化这两个属性. //文件目录    

线程安全变量控制显示隐藏loading框

一.线程安全变量控制显示隐藏loading框 问题描述: 同一页面有两个异步网络请求,第一个请求开始,loading旋转,第二个请求开始loading旋转,第一个结束,loading停止旋转,可是这时第二个请求还没有结束,然后loading就结束了,于是问题就来了. 解决方案: 二.由上面问题引申出的问题: 1. #import <libkern/OSAtomic.h> 这段话是从网上copy过来的,总结了一下原子操作的作用.但是文中提到的osbase.h文件找不到.可能是因为版本升级我的li

线程基础--线程控制

3.  线程控制 1). 线程属性 目标:可以设置 线程的 detached/join 状态,线程栈的大小和最低地址等属性. detached/join 状态的区别: 当线程处于 分离状态(detached)时,线程结束时,os立即回收资源.主线程不可以调用pthread_join获取线程退出时的返回值. 当线程处于 未分离状态(join)时,线程结束时,主线程 调用pthread_join获取线程退出时的返回值, 随后释放该线程资源. a)数据类型 pthread_attr_t b)初始化及释

Java笔记七.线程间通信与线程生命的控制

线程间通信与线程生命的控制 一.线程通信方法 Java是通过Object类的wait.notify.notifyAll这几个方法来实现进程键的通信.由于所有的类都是从Object继承的,因此在任何类中都可以直接使用这些方法. wait:告诉当前线程放弃监视器并进入睡眠状态,知道其他线程进入同一监视器并调用notify为止; notify:唤醒同一对象监视器中调用wait的第一个线程.用于类似饭馆有一个空位后通知所有等候就餐的顾客中的第一位可以入座的情况: notifyAll:唤醒同一对象监视器中

Unix线程概念、控制原语、属性

线程: 线程基础概念: 线程在Linux中又称轻量级进程.并且它和进程都有PCB(进程控制块),但是区别是进程的虚拟地址空间是独享的,也就是每个进程都有自己的虚拟地址空间,但是线程的PCB是共享的,在同一个虚拟地址空间里面,每个线程有自己的PCB.虽然每个线程都有自己的PCB,但是从内核的角度来看,进程和线程是一样的,这是因为同一个虚拟地址空间里面的每个线程的PCB指向的内存资源的三级页表是相同的.在Linux下,可以把线程看做是最小的执行单位(进程内部运用多线程完成任务),而进程是最小的分配资

按键精灵 用全局变量控制线程 子线程控制主线

//************************************************用全局变量控制线程 子线程控制主线 Global b b = 1 线程控制ID = BeginThread(线程控制)//启动线程 While b=1 Delay 2000 Call Plugin.Msg.Tips("我是主线程") wend Rem aaa Delay 8000 While b=2 Delay 2000 Call Plugin.Msg.Tips("我是主线程副

Linux之线程、线程控制、线程属性

一.整体大纲 二.线程相关 1. 什么是线程    LWP:light weight process 轻量级的进程,本质仍是进程(在Linux环境下) 进程:独立地址空间,拥有PCB 线程:也有PCB,但没有独立的地址空间(共享) 区别:在于是否共享地址空间. 独居(进程):合租(线程). Linux下: 线程:最小的执行单位 进程:最小分配资源单位,可看成是只有一个线程的进程. 2. Linux内核线程实现原理     (1)线程实现原理 类Unix系统中,早期是没有“线程”概念的,80年代才

Java并发程序设计(6)线程池之线程数量的控制

1.1. ExecutorService ExecutorService是线程池的接口. Executors是用于创建不同线程池的工具类. 1.2. 线程数量固定的线程池 ExecutorService executorService = Executors.newFixedThreadPool(2); for(int j=0;j<10;j++){ final int t = j; executorService.execute( new Runnable(){ @Override public

07_控制线程_join_线程插队

[join线程简述] join()方法:Thread提供的让一个线程去等待另一个线程完成.当在某个程序执行流中(如main线程)调用其它线程(如t2线程)的join方法(t2.join()),调用线程(main线程)将被阻塞,直到被join()方法加入的join线程(t2.start())执行完成为止. [示例代码] package com.Higgin.part02; class JoinThread implements Runnable{ //重写run方法,定义线程执行体 public

APUE:线程,线程控制

线程标识 pthread_t pthread_self (void); int pthread_equal (pthread_t __thread1, pthread_t __thread2); 创建.退出.等待.取消线程 int pthread_create (pthread_t *__restrict __newthread, const pthread_attr_t *__restrict __attr, void *(*__start_routine) (void *), void *_