草庐IT

同步和异步的一些事

www159 2023-03-30 原文

最近在编程总是会遇到async, Mutex等字眼,今天来盘点以下同步和异步,顺便复习以下操作系统

进程(Process)

正在运行的程序。人们写的程序只定义了入口函数(比如__main_, main(), __main__)。如果执行这个程序,操作系统会为这个程序生成进程。比如在linux中,内核会从0号进程fork()出一个新的进程,接着把我们的代码装载进这个进程,最后这个进程从入口函数开始一步步运行。进程具有一定的资源,比如虚拟内存空间,正在执行程序文件。进程具有一定的标志,比如权限,调度优先级,进程运行状态。

线程(Thread)

考虑到进程的创建和销毁需要维护大量的数据结构(进程表),再加上随着科技的发展,多核cpu逐渐称为主流,为了充分利用多核,人们创造了更小的单位:线程。多个线程能够共享同一个进程的资源。貌似linux在某个版本之后的调度算法将cpu的最小调度单元改成了线程,从而增加了对多核的支持。

协程(Coroutine)

比线程更小的单位。协程的调度并不需要系统调用,而是在一个线程内部实现。相当于一个用户态的的操作系统,但是基本的io操作依赖于线程的io系统调用。主要用来解决io密集线程过多的问题。比如1亿个线程的io读写,那么有很多的时间耗费在了线程切换之上(线程也有数据结构,只是比进程简单, 而且线程的切换是系统调用)。因此人们干脆把调度算法写在线程中,在线程调度协程,那么就只有一个线程,1亿个协程。时间虽然都花在了协程的切换之上,但由于协程的数据结构更简单,且不用频繁系统调用,耗时也减少了。

互斥

资源有限而需求无限。A和B都想买车票,一个售票口只能排队。假如A在B前面,B只能进入等待。

同步(sync)

B在A之后才能执行。

  1. 上面互斥的例子,B只能等待A完成也是一种同步。
  2. 生产者和消费者和商店。消费者到商店购买产品,没有产品只能等生产者造出产品送到商店才行。同步能让消费者进入等待。

异步(async)

B在A之后执行,不过B可以忙别的事。

  1. 上面互斥的例子,B可以刷刷抖音,或者视线排队在边上坐着。等待A的通知,或者售票员喇叭的通知。
  2. 上面同步的第二个例子i,消费者可以刷刷抖音或者在家里躺着。等待生产者的通知,或者商店的短信。

异步的实现

  1. 创建(spawn)一个线程等待资源,主线程干别的事。
  2. 线程获取资源后通过回调函数执行下一步,或者通过消息队列通知主线程。

简化异步

这个困惑了我很久,如今网上的热词如异步线程, 阻塞队列天生并发等等等其实都是说的一件事: 简化异步。众所周知,操作系统已经帮我们实现了线程间异步。以linux为例, 线程天生就有就绪, 等待, 运行三个状态,并且内核也维护了一个阻塞队列。如果从头实现异步,我们需要

  • 创建自己的消息队列
  • 时不时自己管理线程
  • 甚至维护自己的线程池

而这些所谓的高并发语言, 都维护了一个自己的阻塞队列,维护了自己的消息队列,维护了自己的线程池,维护了自己的并发io库,并且拥有自己的异步语法糖。

简化异步的编程范式

以nodejs为例分析简化异步写法的历史与发展,其他的大同小异。

  • 回调函数时期。为了支持异步,于是将IO进行封装,尾巴带了一个回调函数

      // 同步IO函数
      function trueIO(): ResultType {
          ...
          return res;
      }
    
      // 将IO操作和trueIO装入一个线程,并加入阻塞队列
      // 从而不会阻止主线程
      function trueIOAsync(callback: (result: ResultType) => void) {
          block_queue.add(function () {
              const res = trueIO();
              callback(trueIO);
          })
      }
    
      // 没有实现阻塞队列的语言可能会用spawn
      function trueIOAsync(callback: (result: ResultType) => void) {
          spawn(function () {
              const res = trueIO();
              callback(trueIO);
          })
      }
    
      // 调用异步IO函数
      trueIOAsync(function (res) {
          console.log(res);
      })
    

    注:这个trueIOblock_queue.add是我假设的函数

  • Promise时期。后来有人觉得回调函数不太优雅,加上动态语言,函数式思想,builder模式,链式调用的流行,就改变了封装方法,Promise应运而生

    class Promise {
        callback: ((value: any) => void)[];
    
        new(fn: ((result: ResultType) => void, r(err: ErrType) => void) => void) {
           fn(this.innerResolve);
        }
    
        then<T>(fn: (value: T) => void) {
            this.callback.push(fn);
            return this;
        }
    
        innerResolve(result) {
            this.callback.forEach(fn => fn(result));
        }
    }
    

    这样就能够统一管理回调函数,于是可以把回调函数封装成链式调用

     // 封装异步IO函数,加入阻塞队列
     function trueIOPromise() {
         return new Promise(function (res) {
         block_queue.add(function () {
             const res = trueIO();
             res(res);
         });
     })
    
     // 调用异步IO函数
     trueIOPromise().then((res) {console.log(res) });
     }
    

    上面是比较底层的Promise,我们也可以画蛇添足通过异步回调函数构造Promise

    function trueIOPromise() {
        return new Promise(function (res) {
            trueIOAsync(res(res));
        });
    }
    
  • async, await。有人觉得new Promise(...)依旧不够优雅,因此出现了async和await。

    // 借用上面的trueIOPromise()
    async asyncFn() {
        return await trueIOPromise();
    }
    
    // 经过转译后
    function asyncFn() {
        return trueIOPromise();
    }
    
    
    // 第二个例子
    async asyncFn() {
        res = await trueIOPromise();
        res++;
        return res;
    }
    
    // 经过转译后
    function asyncFn() {
        return new Promise(function (resolve) {
            let res = undefined;
            trueIOPromise().then(function (result) {
               res = result;
               res++;
               resolve(res);
            })
        })
    }
    

以上就是三个阶段。至于基于强大过程宏的rustfuture, invoke也是大同小异

有关同步和异步的一些事的更多相关文章

  1. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  2. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

  3. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  4. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

    在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

  5. ruby - 找一些句子 - 2

    我想找到在某些文本中找到一些(让它是两个)句子的好方法。什么会更好-使用正则表达式或拆分方法?你的想法?应JeremyStein的要求-有一些例子示例:输入:ThefirstthingtodoistocreatetheCommentmodel.We’llcreatethisinthenormalway,butwithonesmalldifference.IfwewerejustcreatingcommentsforanArticlewe’dhaveanintegerfieldcalledarticle_idinthemodeltostoretheforeignkey,butinthis

  6. ruby block 并从 block 中返回一些东西 - 2

    我正在使用ruby​​1.8.7。p=lambda{return10;}deflab(block)puts'before'putsblock.callputs'after'endlabp以上代码输出为before10after我将相同的代码重构到这里deflab(&block)puts'before'putsblock.callputs'after'endlab{return10;}现在我收到LocalJumpError:意外返回。对我来说,这两个代码都在做同样的事情。是的,在第一种情况下我传递了一个过程,在第二种情况下我传递了一个block。但是&block将该block转换为pro

  7. ruby - 如果键存在,向散列值添加一些东西? - 2

    我在Ruby中有一个哈希:hash=Hash.new里面有一些键值对,比如说:hash[1]="One"hash[2]="Two"如果散列包含键2,那么我想将“Bananas”添加到它的值中。如果散列没有键2,我想创建一个新的键值对2=>"Bananas"。我知道我可以通过首先使用has_key?检查散列是否具有key2来做到这一点,然后采取相应的行动。但这需要一个if语句和不止一行。那么是否有一种简单、优雅的单行代码可以实现这一目标? 最佳答案 这个有效:hash[2]=(hash[2]||'')+'Bananas'如果您希望所有

  8. ruby-on-rails - 本地 yaml key 的 i18n 同步 - 2

    类似的问题,但对于java,Keepingi18nresourcessynced如何保持i18nyamllocals的key同步?即,当将key添加到en.yml时,如何将它们添加到nb.yml或ru.yml?如果我在my_title:"atitle"旁边添加键my_label:"sometextinenglish"我想把它给我的其他本地人我指定,因为我不能做所有的翻译,它应该回到其他语言的英语例如en.ymlsomegroup:my_tile:"atitleinenglish"my_label:"sometextinenglish"othergroup:...我想发出命令,将整个键和

  9. ruby - 使用什么异步 Ruby 服务器? - 2

    我们开始使用Ruby开发新游戏项目。我们决定使用其中一种异步Ruby服务器,但我们无法决定选择哪一种。选项是:歌利亚抽筋+消瘦/彩虹rack-fiber_pool+rack+thin/rainbowseventmachine_httpserver它们似乎都在处理HTTP请求。Cramp还支持开箱即用的Websocket和服务器端事件。您知道这些服务器的优缺点吗? 最佳答案 我使用eventmachine_httpserver公开了一个RESTfulAPIinanEventMachine-basedIRCbot绝对不会推荐它用于任何严

  10. Ruby 并发/异步处理(简单用例) - 2

    我一直在研究ruby​​的并行/异步处理能力,并阅读了许多文章和博客文章。我查看了EventMachine、Fibers、Revactor、Reia等。不幸的是,我无法为这个非常简单的用例找到简单、有效(且非IO阻塞)的解决方案:File.open('somelogfile.txt')do|file|whileline=file.gets#(R)ReadfromIOline=process_line(line)#(P)Processthelinewrite_to_db(line)#(W)WritetheoutputtosomeIO(DBorfile)endend你看到了吗,我的小脚本正

随机推荐