线程控制

1.线程的概念
main函数和信号处理函数是同⼀个进程地址空间中的多个控制流程,多线程也是如此,但是⽐信号处理函数更加灵活,信号处理函数的控制流程只是在信号递达时产⽣,在处理完信号之后就结束,⽽多线程的控制流程可以长期并存,操作系统会在各线程之间调度和切换,就像在多个进程之间调度和切换⼀样。
有些人把多线程和程序设计和多核系统联系起来,这种想法有些片面。即使程序在单核系统上,也可以做到多线程处理,并且使程序简化,所以处理器的数量并不会影响程序结构,而且,即使多线程程序在串行化任务时不得不阻塞。
同一进程的下的线程共享下面的资源

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

但有些资源是每一个线程所独有的
1.线程id
2.上下文,包括各种寄存器的值,程序计数器和栈指针
3.栈空间
4.errno变量
5.信号屏蔽字
6调度优先级
特点提醒:在linux上线程序函数位于libpthread共享库中,因此在编译时要加上-lpthread选项


2.线程和进程的区别
a.地址空间和其它资源:进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
b.通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
c.调度和切换:线程上下文切换比进程上下文切换要快得多。
d.在多线程中,进程不是一个可执行的实体。
联系:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程全部资源。


3.线程控制的代码
(1)线程的创建

#include<stdio.h>
#include<stdlib.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\n",s,(unsigned long)pid,(unsigned long)tid);
}
void *thr_fn()
{
    printids("new thread:");
    return;
}
int main()
{
    int id=pthread_create(&ntid,NULL,thr_fn,NULL);
    if(id<0){
        perror("pthread_create");
        exit(1);
    }
    printids("main thread:");
    sleep(1);
    return 0;
}

这里写图片描述

int pthread_create(pthread_t *restict tidp,const pthread_attr_t *restrict attr,void* (start_rtn)(void*),void *restrict arg)

线程id是一个用pthread_t数据类型表示的,实现的时候要先定义一个全局的变量,不然打印将没有任何意义。
(2)线程终止
如果需要只终⽌某个线程⽽不终⽌整个进程,可以有三种⽅法:
1. 从线程函数return。这种⽅法对主线程不适⽤,从main函数return相当于调⽤exit。
2. ⼀个线程可以调⽤pthread_cancel终⽌同⼀进程中的另⼀个线程。
3. 线程可以调⽤pthread_exit终⽌⾃⼰。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
void* thread1()
{
    printf("thread1 returning....\n");
    return (void*)1;
}
void* thread2()
{
    printf("thread2 exiting...\n");
    pthread_exit((void*)2);
}
void* thread3()
{
    while(1){
        printf("thread3 is running,wait for be cancal...\n");
        sleep(1);
    }
    return NULL;
}

int main()
{
    pthread_t id;
    void* tret;
    //从线程返回
    pthread_create(&id,NULL,thread1,NULL);
    pthread_join(id,&tret);
    printf("thead return,thread id is:%u,return code is:%d\n",(unsigned int)id,(int)tret);
    //线程自己调用pthread_exit()终止自己
    pthread_create(&id,NULL,thread2,NULL);
    pthread_join(id,&tret);
    printf("thread exit,thread id is:%u,exit code is %d\n",(unsigned int)id,(int)tret);
    //调用pthread_cancel()终止同一进程的另一个线程
    pthread_create(&id,NULL,thread3,NULL);
    sleep(3);
    pthread_cancel(id);
    pthread_join(id,&tret);
    printf("thread3 return,thread id is %u,cancel code is %d\n",(unsigned int)id,(int)tret);
    return 0;
}

这里写图片描述

void pthread_exit(void* retval);

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

void pthread_cancel(pthread_t tid)

该函数来请求取消同一个进程中的其他线程。但是该函数并部等待线程终止,它仅仅提出请求

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

调用该含函数的线程将挂起等待,直到id为thread的线程终止。
4.线程的结合和分离
在任何⼀个时间点上,线程是可结合的(joinable)或者是分离(detached) 。 ⼀个可结合的线程能够被其他线程收回其资源和杀死。在被其他线程回收之前,它的存储器资源(例如栈)是不释放的。 相反, ⼀个分离的线程是不能被其他线程回收或杀死的,它的存储器 资源在它终⽌时由系统⾃动释放。要么被显⽰地回收,即调⽤pthread_join;要么通过调⽤pthread_detach函数被分离。

int pthread_detach(pthread_t tid)

这将该⼦线程的状态设置为分离的(detached),如此⼀来,该线程运⾏结束后会⾃动释
放所有资源.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 C# 中,启动线程控制控件显示可以通过以下步骤实现: 1. 创建一个线程对象,将需要执行的代码放入线程的方法中。 2. 在线程方法中,使用 Control.Invoke 或 Control.BeginInvoke 方法来更新 UI 控件的显示。 3. 启动线程执行方法。 例如,以下是一个简单的示例代码,通过启动一个线程来更新一个 Label 控件的显示: ``` private void btnStart_Click(object sender, EventArgs e) { // 创建线程对象,指定需要执行的方法 Thread thread = new Thread(new ThreadStart(UpdateLabel)); // 启动线程 thread.Start(); } private void UpdateLabel() { // 使用 Invoke 方法来更新 UI 控件的显示 if (lblStatus.InvokeRequired) { lblStatus.Invoke(new Action(() => lblStatus.Text = "正在更新...")); } else { lblStatus.Text = "正在更新..."; } // 模拟耗时操作 Thread.Sleep(5000); // 使用 Invoke 方法来更新 UI 控件的显示 if (lblStatus.InvokeRequired) { lblStatus.Invoke(new Action(() => lblStatus.Text = "更新完成")); } else { lblStatus.Text = "更新完成"; } } ``` 在这个示例中,我们创建了一个线程对象,并将需要执行的方法 UpdateLabel 指定为线程的方法。在 UpdateLabel 方法中,我们使用 Invoke 或 BeginInvoke 方法来更新 Label 控件的 Text 属性的显示。通过调用线程对象的 Start 方法,启动线程执行方法。 需要注意的是,通过 Invoke 或 BeginInvoke 方法更新 UI 控件的显示时,需要检查当前线程是否是 UI 线程,如果不是则需要使用 Invoke 方法来将更新操作委托到 UI 线程上执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值