精华内容
下载资源
问答
  • 可重入和不可重入函数异步信号安全函数线程安全函数
    千次阅读
    2019-04-03 13:17:58

    要解释可重入函数为何物,首先需要区分单线程程序和多线程程序。典型UNIX程序都具有一条执行线程,贯穿程序始终,CPU围绕单条执行逻辑来处理指令。而对于多线程程序而言,同一进程却存在多条独立、并发的执行逻辑流。

    如果同一个进程的多条线程可以同时安全的调用某一函数,那么该函数就是可重入的。此处,“安全”意味着,无论其他线程调用该函数的执行状态如何,函数均可产生预期结果。

    SUSv3对可重入函数的定义是:函数有两条或多条线程调用时,即便是交叉执行,其效果也与各线程以任意顺序依次调用时一致。

    更新全局变量或者静态数据结构的函数可能是不可重入的。(只用到本地变量的函数肯定是可重入的。)

    C语言标准函数库中,malloc和free就维护有一个针对已释放内存块的链表,用于从堆中重新分配内存。如果主程序在调用malloc期间为一个同样调用malloc的信号处理器函数所中断,那么该链表可能会遭到破坏。因此,malloc函数族以及使用它们的其他库函数都是不可重入的。


    异步信号安全函数是指当信号处理器函数调用时,可以保证其实现是安全的。如果某一函数是可重入的,又或者信号处理器函数无法将其中断时就称该函数是异步信号安全的。


    若函数可同时供多个线程安全调用,则称之为线程安全函数;反之,如果函数不是线程安全的,则不能并发调用。

    由此可以看出,可重入函数一定是线程安全函数,不可重入函数也可能是线程安全函数,例如,malloc函数是不可重入函数,但是malloc函数通过使用互斥量实现了线程安全。

    更多相关内容
  • 主要介绍了C#中异步回调函数用法,实例分析了异步回调函数的定义及使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
  • 首先先明白什么样的函数不是线程安全的:使用多个thread共享的全局或静态变量;函数中调用了非thread_safe的子函数。 C库函数中的线程安全性 在 ARM 库中,函数可能是线程安全的,如下所示: 某些函数从来都...

    首先先明白什么样的函数不是线程安全的:使用多个thread共享的全局或静态变量;函数中调用了非thread_safe的子函数。

     

    C库函数中的线程安全性

    在 ARM 库中,函数可能是线程安全的,如下所示:

    • 某些函数从来都不是线程安全的,示例 setlocale()

    • 某些函数在本质上就是线程安全的,示例 memcpy()

    • 某些函数(示例 malloc())可通过实现 _mutex_*
      函数变为线程安全的函数

    • 其他函数仅在传递了适当参数时才是线程安全的,示例 tmpnam()

    如果应用程序以隐藏方式使用 ARM 库(如使用语言辅助函数),则可能会出现线程问题。

    线程安全的函数

    Table 1 显示了线程安全的
    C 库函数。

    Table 1. 线程安全的函数

    函数说明
    calloc(),
    
    free(),
    
    malloc(),
    
    realloc()
    

    如果实现了 _mutex_* 函数,则堆函数是线程安全的。

    在所有线程之间共享单个堆,并使用互斥量以避免进行并发访问时发生数据损坏。 每个堆实现都负责进行自己的锁定。 如果您提供了自己的分配器,它也必须进行自己的锁定。 这样,它便可进行精细锁定(如果需要),而不是简单地使用单个互斥量保护整个堆(粗放锁定)。

    __alloca(),
    
    __alloca_finish(),
    
    __alloca_init(),
    
    __alloca_initialize()
    

    如果实现了 _mutex_* 函数,则 alloca 函数是线程安全的。

    每个线程的 alloca 状态包含在 __user_perthread_libspace 块中。
    这意味着多个线程不会发生冲突。

    Note

    请注意,alloca 函数也使用堆。 不过堆函数都是线程安全的。

    abort(),
    
    raise(),
    
    signal(),
    
    fenv.h
    

    ARM 信号处理函数和 FP 异常捕获是线程安全的。

    信号处理程序和 FP 捕获设置是整个进程中的全局设置,并使用锁对其进行保护。 这样,即使多个线程同时调用 signal() 或 fenv.h 函数,也不会损坏数据。
    但要注意,调用影响所有线程,而不是只影响调用线程。

    clearerr(), fclose(),
    
    feof(),ferror(), fflush(),
    
    fgetc(),fgetpos(), fgets(),
    
    fopen(),fputc(), fputs(),
    
    fread(),freopen(), fseek(),
    
    fsetpos(),ftell(), fwrite(),
    
    getc(),getchar(), gets(),
    
    perror(),putc(), putchar(),
    
    puts(),rewind(), setbuf(),
    
    setvbuf(),tmpfile(), tmpnam(),
    
    ungetc()
    

    如果实现了 _mutex_* 函数,则 stdio 库是线程安全的。

    每个单独的流都使用锁进行保护,因此,两个线程可以分别打开并使用其自己的 stdio 流,而不会相互干扰。

    如果两个线程都要读取或写入相同的流,fgetc() 和 fputc() 级别的锁定可防止发生数据损坏,但是,每个线程的单独字符输出可能会交叉出现,因而容易造成混淆。

    Note

    请注意,tmpnam() 也包含一个静态缓冲区,但仅在自变量为
    NULL 时才使用它。 要确保tmpnam() 使用是线程安全的,应提供您自己的缓冲区空间。

    fprintf(), printf(),
    
    vfprintf(), vprintf(), fscanf(),
    
    scanf()
    

    使用这些函数时:

    • 标准 C printf() 和 scanf() 函数使用 stdio,因而是线程安全的。

    • 如果在多线程程序中调用标准 C printf(),其语言环境可能会发生变化。

    clock()
    
    clock() 包含程序静态数据,此数据是在启动时一次性写入的,以后只能对其进行读取。 因此,clock() 是线程安全的,但前提是在初始化库时没有运行任何其他线程。
    errno()
    

    errno 是线程安全的。

    每个线程将其自己的 errno 存储在 __user_perthread_libspace 块中。
    这意味着,每个线程可以单独调用 errno 设置函数,然后检查 errno,而不用担心受其他线程的影响。

    atexit()
    

    atexit() 维护的退出函数列表是进程全局性的,并且使用锁对其进行保护。

    在最坏的情况下,如果多个线程调用 atexit(),则不能保证调用退出函数的顺序。

    abs(), acos(), asin(),atan(),
    
    atan2(), atof(),atol(), atoi(),
    
    bsearch(),ceil(), cos(),
    
    cosh(),difftime(), div(),
    
    exp(),fabs(), floor(),
    
    fmod(),frexp(), labs(),
    
    ldexp(),ldiv(), log(),
    
    log10(),memchr(), memcmp(),
    
    memcpy(),memmove(), memset(),
    
    mktime(),modf(), pow(),
    
    qsort(),sin(), sinh(),
    
    sqrt(),strcat(), strchr(),
    
    strcmp(),strcpy(), strcspn(),
    
    strlcat(),strlcpy(), strlen(),
    
    strncat(),strncmp(), strncpy(),
    
    strpbrk(),strrchr(), strspn(),
    
    strstr(),strxfrm(), tan(), tanh()
    

    这些函数在本质上就是线程安全的。

    longjmp(), setjmp()
    

    虽然 setjmp() 和 longjmp() 在 __user_libspace 中保存数据,但它们均调用线程安全的__alloca_* 函数。

    remove(), rename(), time()
    

    这些函数使用中断,以便与 ARM 调试环境进行通信。 通常,必须为实际应用程序重新实现这些函数。

    snprintf(), sprintf(),
    
    vsnprintf(),vsprintf(), sscanf(),
    
    isalnum(),isalpha(), iscntrl(),
    
    isdigit(),isgraph(), islower(),
    
    isprint(),ispunct(), isspace(),
    
    isupper(),isxdigit(), tolower(),
    
    toupper(),strcoll(), strtod(),
    
    strtol(),strtoul(), strftime()
    

    使用这些函数时,这些基于字符串的函数将读取语言环境。 通常,它们是线程安全的。 但是,如果在会话中更改语言环境,则必须确保这些函数不受影响。

    基于字符串的函数并不依赖于 stdio 库,示例,sprintf() 和 sscanf()

    stdin, stdout, stderr
    
    这些函数是线程安全的。

     

    FP 状态字

    可以在多线程环境(甚至软件浮点)中安全地使用 FP 状态字。 其中,每个线程的状态字存储在其自己的 __user_perthread_libspace 块中。

    Note

    请注意,在硬件浮点中,FP 状态字存储在 VFP 寄存器中。 在这种情况下,线程切换机制必须为每个线程保留该寄存器的单独副本。

    非线程安全的函数

    Table 2.2 显示了非线程安全的
    C 库函数。

    Table 2. 非线程安全的函数

    函数说明
    setlocale()
    

    语言环境设置是所有线程的全局设置,并且未使用锁对其进行保护。 如果两个线程调用 setlocale(),则可能会发生数据损坏。
    另外,很多其他函数读取当前语言环境设置,示例,strtod() 和 sprintf()
    因此,如果一个线程调用 setlocale(),另一个线程同时调用此函数,则可能会产生意外结果。

    ARM 建议您选择所需的语言环境,然后调用一次 setlocale() 以对其进行初始化。 应在程序中创建任何其他线程之前执行此操作,以使任意数量的线程可以同时读取语言环境设置,而不会相互干扰。

    请注意,localeconv() 不是线程安全的。 应改用指向用户提供的缓冲区的指针调用 ARM 函数 _get_lconv()

    asctime(), localtime(),
    
    strtok()
    

    这些函数不是线程安全的。 每个函数都包含一个静态缓冲区,其他线程可能会在调用函数以及随后使用其返回值之间覆盖该缓冲区。

    ARM 提供了可重入版本 _asctime_r()_localtime_r() 和 _strtok_r()
    ARM 建议您改用这些函数以确保安全。

    Note

    这些可重入版本使用一些附加参数。_asctime_r() 使用的附加参数是指向输出字符串要写入的缓冲区的指针。_localtime_r() 使用的附加参数是指向结果要写入的 struct
    tm
     的指针。_strtok_r() 使用的附加参数也是一个指针,指向的是指向下一个标记的 char 指针。

    gamma()[1], lgamma()
    
    这些扩展 mathlib 函数使用全局变量 _signgam,因此不是线程安全的。
    mbrlen(), mbsrtowcs(),
    
    mbrtowc(),wcrtomb(),
    
    wcsrtombs()
    

    stdlib.h 中定义的 C89 多字节转换函数(如 mblen() 和 mbtowc())不是线程安全的,因为它们包含在所有线程之间共享而没有锁定的内部静态状态。

    但是,wchar.h 中定义的扩展可重启版本(示例,mbrtowc() 和 wcrtomb())是线程安全的,但前提是您传入指向您自己的 mbstate_t 对象的指针。
    如果要在处理多字节字符串时确保线程安全,这些函数只能使用非 NULL 的 mbstate_t * 参数。

    exit()
    

    即使提供了基本 _sys_exit()(实际终止所有线程)的实现,也不要在多线程程序中调用 exit()

    在这种情况下,exit() 在调用 _sys_exit() 之前 先执行清除操作,因此会中断其他线程。

    rand(), srand()
    

    这些函数保留全局性且不受保护的内部状态。 这意味着,rand() 调用从来都不是线程安全的。

    ARM 建议您使用自己的锁定,以确保每次只有一个线程调用 rand(),示例,通过定义 $Sub$$rand()(如果要避免更改代码)。

    或者,也可以执行以下操作之一:

    • 提供您自己的随机数生成器,它可能具有多个独立实例

    • 硬性规定只有一个线程需要生成随机数。

    翻译自:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0492c/Chdiedfe.html 

    展开全文
  • 实现PHP多线程异步请求的方法有很多,在本文整理了3种不多的常用方法,大家可以参考下
  • C#中的异步线程

    千次阅读 2018-07-25 14:46:45
    1.通过委托,开启异步线程  Action是委托类型,BeginInvoke 开启异步线程。正常情况下,Main线程是顺序执行的,但是开启异步线程后,程序的执行顺序由调度决定。执行结果可能先输出"Main",后输出"...

    1.通过委托,开启异步线程

      Action是委托类型,BeginInvoke 开启异步线程。正常情况下,Main线程是顺序执行的,但是开启异步线程后,程序的执行顺序由调度决定。执行结果可能先输出"Main",后输出"Method",也可能是相反顺序。

    static void Method(string str)
    {
        Console.WriteLine(str);
    }
    static void Main(string[] args)
    {           
        Action<string> action = Method;
        //BeginInvoke 开启异步线程
        action.BeginInvoke("Method", null, null);
        Console.WriteLine("Main");          
    }

    2.获得异步线程的返回值

       想要获得异步线程的返回值,需要判断线程何时结束,判断异步线程结束的方法有:while循环,等待句柄,回调函数等。

    2.1 while循环

       BeginInvoke的返回值是IAsyncResult,表示当前线程的状态。IsCompleted表示当前线程是否结束。当线程结束后,调用EndInvoke 获得线程的返回值。

       当函数执行时间比较长时,采用异步线程比较合适,比如下载,网络传输等。下面例子中计算整数的平方,不会花费太长时间,因此使用Thread.Sleep(10)使线程暂停10毫秒。

    class Program
    {
        static int Square(int num)
        {
            Thread.Sleep(10);
            return num * num;
        }
        static void Main(string[] args)
        {
            Func<int, int> func = Square;
            IAsyncResult async = func.BeginInvoke(10, null, null);  //IAsyncResult 表示当前线程的状态
            while (!async.IsCompleted)
            {
                Console.Write(".");
            }
            //EndInvoke 获得异步线程的返回值
            int result = func.EndInvoke(async);
            Console.WriteLine("result = " + result);
    
            Console.ReadKey();
        }
    }

    执行结果

    2.2 等待句柄

       AsyncWaitHandle用于等待异步操作完成的WaitHandle,WaitOne阻止当前线程,直到WaitHandle收到信号,参数为设置的超时时间。如果该时间内线程结束,则返回ture,否则返回false。

    class Program
    {
        static int Square(int num)
        {
            Thread.Sleep(10);
            return num * num;
        }
        static void Main(string[] args)
        {
            Func<int, int> func = Square;
            IAsyncResult async = func.BeginInvoke(10, null, null);  
            bool flag = async.AsyncWaitHandle.WaitOne(1000);
            if (flag)
            {
                int result = func.EndInvoke(async);
                Console.WriteLine("result = " + result);
            }
    
            Console.ReadKey();
        }
    }

    2.3 回调函数

          BeginInvoke倒数第二个参数是一个委托,当线程结束时,就调用该委托指向的回调方法。最后一个参数给回调方法传递数据。

           OnCallBack是回调函数,线程结束时执行。func是传递给回调方法的参数。

    class Program
    {
        static int Square(int num)
        {
            Thread.Sleep(10);
            return num * num;
        }
        static void OnCallBack(IAsyncResult async)
        {
            Func<int, int> function = async.AsyncState as Func<int, int>;
            int result = function.EndInvoke(async);
            Console.WriteLine("在回调函数中取得结果:" + result);
        }
    
        static void Main(string[] args)
        {
            Func<int, int> func = Square;
            func.BeginInvoke(10, OnCallBack, func);
    
            Console.ReadKey();
        }
    }

    2.4 Lambda表达式

        Lambda表达式可以访问外部数据,所以func不再需要作为参数传递,BeginInvoke的最后一个参数设为null。

    class Program
    {
        static int Square(int num)
        {
            Thread.Sleep(10);
            return num * num;
        }
        static void Main(string[] args)
        {
            Func<int, int> func = Square;
            func.BeginInvoke(10, async =>
            {
                int result = func.EndInvoke(async);
                Console.WriteLine("在Lambda表达式取得结果:" + result);
            }, null);
    
            Console.ReadKey();
        }
    }

     

    展开全文
  • python多线程异步(一)

    千次阅读 2021-11-28 20:18:30
    目录1、多线程异步初步介绍1.1一般的异步demo1.2傀儡线程2、线程锁2.1、为什么要锁2.2、不加锁代码2.3、加锁代码3、条件锁 1、多线程异步初步介绍 串行和异步模式如下图,从图上可以很直观看出串行变成和异步编程...

    一直想写一个多线程博客,汇总一下方老师教给我们的知识。但是因为一直没有用到,或者自己还没有吃透里面的精髓,所以不敢下笔。现在工作中又遇到必须要通过多线程解决的问题,所以再回顾以前方老师的课程,从头整理一下多线程异步这块知识,方便以后遇到问题可以快速写出代码来。

    1、多线程异步初步介绍

    串行和异步模式如下图,从图上可以很直观看出串行变成和异步编程区别
    在这里插入图片描述
    python 中线程需要用到自带的Threading包,可以使用并发

    1.1一般的异步demo

    import time
    import threading
    
    def syn_method():#串行的普通编程方式
        print("Start")
    
        time.sleep(1)
        print("Visit website 1")
    
        time.sleep(1)
        print("Visit website 2")
    
        time.sleep(1)
        print("Visit website 3")
    
        print("End")
    
    def asyn_method():#异步多线程方式
        print("Start")
    
        def visit1():#注意,这里是在asyn_method函数内部定义visit1函数,在python中是允许这样写的
            time.sleep(1)
            print("Visit website 1")
    
        def visit2():
            time.sleep(1)
            print("Visit website 2")
    
        def visit3():
            time.sleep(1)
            print("Visit website 3")
    
        th1 = threading.Thread(target=visit1)#首先定义多线程
        th2 = threading.Thread(target=visit2)
        th3 = threading.Thread(target=visit3)
    
        th1.start()#其次运行线程,相当于快速按下线程开关,线程启动后继续异步向下执行下面代码
        th2.start()
        th3.start()
    
        th1.join()#最后汇总,等待线程1完成
        th2.join()
        th3.join()
    
        print("End")
    
    asyn_method()#异步多线程函数
    # syn_method()#同步串行函数
    

    上面这个代码包含了线程定义、线程开始、线程阻塞执行三个要点。

    在这里插入图片描述
    在这里插入图片描述

    1.2傀儡线程

    在异步执行的时候,主线程执行完毕后(子线程没有join等待完成),有些子线程可能还没有启动,这时候就需要在主线程执行完毕后把所有没用启动或者正在执行的线程都杀死,避免造成系统垃圾。这时候就要用到傀儡线程,如果设置了傀儡线程,当主线程执行完毕后傀儡线程会自动关闭
    傀儡线程的设置很简单,只需要在设置线程的时候增加daemon=True即可
    把上面代码做了一下改进,1、把join()阻塞代码注释掉;2、在定义线程的时候增加daemon=True;3把sleep时间增加到10秒,便于更好的实验观察
    代码如下:

    import time
    import threading
    
    def syn_method():#串行的普通编程方式
        print("Start")
    
        time.sleep(1)
        print("Visit website 1")
    
        time.sleep(1)
        print("Visit website 2")
    
        time.sleep(1)
        print("Visit website 3")
    
        print("End")
    
    def asyn_method():#异步多线程方式
        print("Start")
    
        def visit1():#注意,这里是在asyn_method函数内部定义visit1函数,在python中是允许这样写的
            time.sleep(10)
            print("Visit website 1")
    
        def visit2():
            time.sleep(10)
            print("Visit website 2")
    
        def visit3():
            time.sleep(10)
            print("Visit website 3")
    
        th1 = threading.Thread(target=visit1,daemon=True)#设置daemon=True,把线程定义成傀儡线程,当主线程结束后,傀儡线程自动关闭,而不会在后台运行
        th2 = threading.Thread(target=visit2,daemon=True)
        th3 = threading.Thread(target=visit3,daemon=True)
    
        th1.start()#其次运行线程,相当于快速按下线程开关,线程启动后继续异步向下执行下面代码
        th2.start()
        th3.start()
    
        #th1.join()#最后汇总,等待线程1完成
        #th2.join()
        #th3.join()
    
        print("End")
    
    asyn_method()#异步多线程函数
    # syn_method()#同步串行函数
    

    增加了傀儡设置后,运行结果如下
    在这里插入图片描述
    各个子线程的结果没用输出,因为在等待的过程中主线程已经执行完毕了,各个子线程被杀死了。
    实验二:把傀儡线程代码daemon=True删掉,或者改为False,结果如下
    在这里插入图片描述
    主线程执行完,但是程序并没有真正结束,等待一会儿子线程结果输出了,程序结束。
    在这里插入图片描述

    在这里插入图片描述

    2、线程锁

    2.1、为什么要锁

    为什么要线程锁,只要有线程定义、线程开始、线程阻塞执行三个要点就够了吗?答案是肯定不够的
    我们来看一个demo
    该demo功能是一个多线程累加和一个多线程累减,
    按照上面已经学到的知识可以很轻松写出下面代码

    2.2、不加锁代码

    import threading
    
    n=0
    def add(num):#累加的函数
        global n
        for _ in range(num):
            n+=1
    
    def sub(num):#减的函数
        global n
        for _ in range(num):
            n -= 1
    
    num = 10000000
    th_add = threading.Thread(target=add,args=(num,))#定义线程,把num传参进去
    th_sub = threading.Thread(target=sub,args=(num,))
    
    th_add.start()#启动线程
    th_sub.start()
    
    th_add.join()#阻塞执行
    th_sub.join()
    
    print("n=",n)
    print("End")
    
    

    简单分析一下,定义了一个多线程的加函数,又定义了一个多线程减函数,分别启动后阻塞等待程序执行完,不管输入的数字是多少,期待的结果总是为0才对,执行完毕后,结果如下:
    在这里插入图片描述
    n为啥不等于0,多换几个数字,依然不等于0
    原因分析:
    在这里插入图片描述
    假设n=100的时候,首先加法线程取出100,然后进行加一操作等于101,但是这时候异步在执行,没有等把101结果返回给n的时候减法函数从内存中取出了n值为100,同样进行了减1操作,结果等于99,这时候99的结果和101结果同时赋值给n,所以这时候n的结果不是99就是101,但是不管结果是99或者101,这个时候n值已经错了(n值原来为100,加了一个数,然后又减了一个数,结果应该还是100才对)。
    所以这时候就要用到线程锁来协调数据,在进行加法或者减法操作的时候希望n值是计算完成,写进去了,这样就不会乱了。
    先在开头定义一把锁:lock = threading.Lock()。加锁有两种写法,可以用 lock.acquire()加锁,lock.release()解锁方法;或者直接with lock方法。
    下面demo分别演示了两种加锁方式

    2.3、加锁代码

    import threading
    lock = threading.Lock()#在开头定义一把锁
    n=0
    def add(num):#加的函数
        global n
        for i in range(num):
            lock.acquire()#加锁方法一。相当于java 的 lock.lock()
            try:
                n+=1
            finally:
                lock.release()#解锁
    
    def sub(num):#减的函数
        global n
        for _ in range(num):
            with lock:#加锁方法二
                n -= 1
    
    num = 10000000
    th_add = threading.Thread(target=add,args=(num,))#定义线程,把num传参进去
    th_sub = threading.Thread(target=sub,args=(num,))
    
    th_add.start()#启动线程
    th_sub.start()
    
    th_add.join()#阻塞执行
    th_sub.join()
    
    print("n=",n)
    print("End")
    

    不管num是多少,结果为0。符合预期结果
    在这里插入图片描述

    3、条件锁

    有锁就够了吗,感觉还不够方便。因为如果我有一个缓冲区(缓冲区可以是个有限长度的列表,也类似于一个内存,内存容量是有限的,缓冲区的容量同样是指有限容量的空间),要在缓冲区内想利用多线程写进数据,同时也想从缓冲区获取数据,获取完数据就删除数据,释放缓冲区空间。缓冲区有大小,所以如果已经满了就写不进去,如果缓冲区没用数据,则获取失败。该问题就是生产者和消费者问题,或者也叫缓冲区问题。这时候就要用到条件锁
    条件锁的定义方法:
    has_data = threading.Condition(lock)#里面要传入一个锁,构成条件锁
    has_loc= threading.Condition(lock)
    一个lock可以有很多Condition,一个Condition只有一个lock。
    Condition里面有个wait方法进行条件阻塞,配合if可以实现带条件的锁定和解锁。
    阻塞的wait方法返回条件有两个,一是激活notify_all;二是timeout时间到了;
    例如下面一个样例:1个lock,赋给了两个Condition,一个是C1,一个是C2;C1下面又有两个线程与其有关th1、th2;C2下面又有两个线程与其有关th3、th4、th5。如果这时候notify_all C1,则下面的th1和th2都活过来了。如果这时候notify_all C2,则th3、th4、th5都活过来了。
    在这里插入图片描述

    3.1、条件锁代码

    下面看看代码

    #程序功能:生产者消费者问题
    import threading
    
    class Buffer:#定义缓冲区类
        def __init__(self,buffer_size:int):
            self.buffer_size = buffer_size#缓冲区大小
            self.buffer = []#用一个列表模拟缓冲区
            lock = threading.RLock()#RLock允许在lock中进行第二次调用acquire()进行锁定,而如果是普通lock,不允许在lock中套入lock的acquire(),这样造成锁死(简单的说:一个锁把带有钥匙的主人给锁住了)。所以一般的RLock比较常用,这种lock相当于java ReentrantLock
            self.has_lock = threading.Condition(lock)#条件锁定义,表示有空位
            self.has_data = threading.Condition(lock)#条件锁定义,表示有数据
    
    
        def put(self,data,blocking = True):#向buffer传入数据,生产者。blocking是判定有没有缓存空间写入数据
            #return True if the data is put into this buffer successfully,False otherwise
            with self.has_lock:
                if blocking:
                    while len(self.buffer)>=self.buffer_size:#条件锁的条件
                        self.has_lock.wait()#阻塞has_lock
                else:
                    if len(self.buffer) >= self.buffer_size:
                        return False
                self.buffer.append(data)
                self.has_data.notify_all()#激活has_data条件锁
                return True
    
    
    
        def get(self,blocking = True):#取数据(消费者)
            with self.has_data:
                if blocking:
                    while len(self.buffer)==0:#取数据等待的条件是缓冲区没用数据
                        self.has_data.wait()#阻塞has_data
                else:
                    if len(self.buffer)==0:
                        return False
                result = self.buffer[0]
                del self.buffer[0]
                self.has_lock.notify_all()#删除了取出的数据,说明有空位了,这时候激活has_lock
                return result
    
    if __name__ == '__main__':
        num = 20
        buffer= Buffer(5)
        def produce(n:int):#生产函数
            for i in range(num):
                data = "data_%d_%d"%(n,i)
                buffer.put(data)
                print(data,"is produced.")
    
        def resume():#消费函数
            for _ in range(num):
                data = buffer.get()
                print(data," is resume.")
    
        th0 = threading.Thread(target=produce,args=(0,))#定义一个多线程,调用生产的函数
        th1 = threading.Thread(target=produce,args=(1,))
        th2 = threading.Thread(target=resume)#定义一个多线程,调用消费的函数
        th3 = threading.Thread(target=resume)
        th0.start()
        th1.start()
        th2.start()
        th3.start()
        th0.join()
        th1.join()
        th2.join()
        th3.join()
        print("The test is End")
    

    就这样吧,后面再在开新文章,增加几个多线程案例。

    展开全文
  • 可重入函数 当一个被捕获的信号被一个进程处理时,进程执行的普通的指令序列会被一个信号处理器暂时地中断。它首先执行该信号处理程序中的指令。如果从信号处理程序返回(例如没有调用exit或longjmp),则继续执行在...
  • 一、node单线程实现高并发原理 众所周知nodejs是单线程且支持高并发的脚本语言。可为什么单线程的nodejs可以支持高并发...// 第二步:发出指令,然后把回调函数加入异步队列(回调函数并没有执行) setTimeout(() => {
  • 异步任务实现为同步的方式:信号量、派发组、......
  • 《多线程编程指南》中定义,可以被信号控制器安全调用的函数被称为"异步信号安全"函数。 因此,我认为可重入与异步信号安全是一个概念。 有人将可重入函数线程安全函数混为一谈,我认为是不正确的
  • springboot 开启一个异步线程

    千次阅读 2020-12-26 22:27:28
    在Application类上加上 @EnableAsync注解开启异步 在被调用的方法上面加上@Async,也可以直接在类上加此注解,会标识所有方法为异步方法 如下方式会使@Async失效 异步方法使用static修饰 异步类没有使用@...
  • 异步函数被挂起时,函数线程会被阻塞。 当异步函数恢复时,从它调用的异步函数返回的结果流回原始函数,并从上次停止的地方继续执行。 // 惯例来个Person类 class Person { class func waitResult() -> ...
  • 可重入函数异步信号安全函数

    千次阅读 2019-04-14 14:48:00
     如果一个函数能被多个线程同时调用且不发生竞态,他就是线程安全的,或称为可重入的。  被不同的控制流程调用,有可能在第一次调用还没返回时就再次进入该函数这称为重入,访问一个全局对象,有可能因为重入而...
  • Spring Boot启用异步线程

    万次阅读 2018-10-31 14:31:14
    现在实际开发中常用的方式是采用JMS消息队列方式,发送消息到其他的系统中进行导出,或者是在项目中开启异步线程来完成耗时的导出工作。本文将结合文本导出的场景,来讲解一些Spring Boot中如何开启...
  • C# 异步线程调用方法 实现

    千次阅读 2019-08-01 18:00:38
    1:使用线程Thread调用。 //实例化类 起始我们也可以调用同一个类中的函数 这位师傅就是这么演示一下 ThreadTest test = new ThreadTest(); //创建thread类 构造函数的参数为我们的目标函数 Thread thread1 = ...
  • Java8 异步线程

    万次阅读 2018-07-03 13:49:25
    1. 采用ThreadLocal(本地线程变量)将服务的请求参数信息维护在每一个线程的独享变量。² 减少对共享资源同步机制处理,² 减少代码结构复杂化,做到松耦合。(原因是:因为业务代码每层中处理都是针对接口请求...
  • 一直没弄明白异步执行的线程之间,如果一个线程要等待其它必要的线程先完成才能继续执行这个线程的话,该如何做呢?直到到论坛问了一些大神,才得到点启示。形成实例,却又状况百出。直到今天,终于弄出想要的效果了...
  • 1.js中多线程-单线程, 同步-异步,阻塞-非阻塞, 回调函数的关系理解 //2018.12.12 1.多线程/单线程 简单理解为: 多线程:程序可以同一时间做几件事. 单线程:程序同一时间只能做一件事. 在...
  • C#多线程异步

    万次阅读 多人点赞 2018-04-25 22:12:43
    一、使用计时器在某些情况下,可能不需要使用单独的线程。如果应用程序需要定期执行简单的与 UI 有关的操作,则应该考虑使用进程计时器。有时,在智能客户端应用程序中使用进程计时器,以达到下列目:• 按计划定期...
  • C#异步线程的实现

    千次阅读 2021-02-04 17:50:58
    目录同步与异步的区别异步的实现方式delegate委托的异步调用BeginInvokeCallBack: BeginInvoke执行结束会自动执行回调函数EndInvoke在线程池种清理完成的异步方法ThreadTaskThreadPoolParallelasync&...
  • c++11多线程中的std::async异步函数

    千次阅读 2019-01-10 22:12:26
    目录 写在前面 解析 ...如果有一个需要长时间运算的线程,需要计算出最终的有效值,但是现在不迫切需要这个数值,如果我们使用std::thread的话,着一定是不可行的,因为std::thread不可以接收返回...
  • 同步函数异步函数

    千次阅读 2019-11-01 01:09:57
    同步、异步概念 定义:同步和异步关注的是消息通信机制(synchronous communication/ asynchronous communication)。从程序同步,就是调用方发起某个调用后,调用方得等待这个调用返回结果才能继续往下执行。异步,...
  • Window线程线程操作函数

    万次阅读 2020-06-21 02:58:58
    创建线程 HANDLE hThread1 = ::CreateThread(NULL, 0, ... LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全属性 通常为NULL SIZE_T dwStackSize, // 参数用于设定线程可以将多少地址空间用于它自己的堆栈
  • python如何实现异步调用函数执行

    千次阅读 2020-02-27 21:59:46
    这篇文章主要介绍了python如何实现异步调用函数执行,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋在 实现异步调用之前我们先进行什么是同步调用和异步调用 同步:是指完成...
  • 异步执行函数线程Dome 调用MSVC CRT的函数_beginthread()或_beginthreadex()来创建线程。 _beginthread 参数和返回值 unsigned long _beginthread( void(_cdecl *start_address)(void *), //声明为void (*start_...
  • 异步和多线程学习(一)

    千次阅读 2018-07-03 17:08:21
    零、学习目录:1 进程-线程-多线程,同步和异步2 委托启动异步调用3 多线程特点:不卡主线程、速度快、无序性4 异步的回调和状态参数5 异步等待三种方式6 异步返回值一、进程-线程-多线程,同步和异步1.什么是进程?...
  • 最近在研究Spring Boot中的异步处理,发现涉及到异步和多线程的很多知识点,就先写几篇关于异步与多线程的文章,带大一起回顾或学习一下相关的知识点。下面开始正文内容: 前言 在本文中,我们通过一些通俗易懂的...
  • .NET 异步线程

    千次阅读 2019-02-28 21:09:57
    记录一下异步线程的进阶历史,以及简单的使用方法 主要还是以Task,Parallel为主,毕竟用的比较多的现在就是这些了,再往前去的,除非是老项目,不然真的应该是挺少了,大概有个概念,就当了解一下进化史了 1、...
  • Flutter开发之Dart线程异步

    千次阅读 2019-10-18 14:04:06
    谈到异步,可能大家多会想到多线程,然而Dart是基于事件循环机制的单线程模型。单线程?嗯哼,也就是说在Dart的世界里没有多线程之说,当然也没有了所谓的主线程和子线程之分。
  • JS的异步和同步函数

    千次阅读 2019-08-20 14:49:09
    要弄清楚同步和异步的问题,必须搞清楚js的单线程,以前看书和博客常常看到说“JavaScript是一种异步线程的变成语言”这句话,以前理解的不太深入,今天通过查询资料,重新学习了一遍, 1. js...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 261,809
精华内容 104,723
关键字:

异步线程安全函数