精华内容
参与话题
问答
  • Worker

    2020-01-21 17:28:34
    worker JavaScript 是一门基于单线程异步操作的语言, 这是为了避免多线程带来的复杂性. 对于一些好事的网络请求, 在 JS 中一般都使用异步执行来避免阻塞. 但是, JS 中也是可以开启其他线程的, 也就是 worker. 可以说...

    worker

    JavaScript 是一门基于单线程异步操作的语言, 这是为了避免多线程带来的复杂性.
    对于一些好事的网络请求, 在 JS 中一般都使用异步执行来避免阻塞.
    但是, JS 中也是可以开启其他线程的, 也就是 worker. 可以说这就是 JS 中的多线程.
    worker 虽然可以开启主线程以外的线程执行一些其他操作, 但是它和主线程还是有区别的. 应该理解为辅助线程

    与主线程的区别

    1. 上下文对象不同

    通俗讲就是 this 指向不同, 主线程的 this 执行 window 对象(函数内 this 另当别论), 而 worker 线程 this 指向一个 DedicateWorkerGlobalScope 对象. 在 worker 线程中无法使用 window 对象, 在 worker 线程访问 window 会报 window is not defined 异常, 但是仍然可以使用 window 下的许多对象和函数.

    2. 无法操作 DOM 元素

    在 worker 线程中无法去操作页面的元素, 因为多个线程同时操作 dom 可能导致页面错误, 而且会使程序变得复杂, 所以在 JS 中, 限制了主线程以外的线程操作 dom 元素

    创建线程

    创建 worker 使用 window 对象的 Worker 构造函数, Worker 接受一个 JS 文件的 URL 作为参数, 该 JS 文件代码将在 worker 线程中执行

    main.js

    var worker = new Worker('./worker');
    

    worker.js

    console.log('worker start');
    setInterval(function() {
      console.log('worker working');
    }, 10000);
    

    终止线程

    终止线程可以在 main.js 中调用 worker 对象的 terminate 函数, 也可以在 worker 线程(worker.js)中调用 worker 上下文对象的 close 函数

    terminate

    main.js

    var worker = new Worker('./worker');
    // 5秒后终止线程
    setTimeout(function() {
      worker.terminate();
    }, 5000);
    

    worker.js

    console.log('worker start');
    setInterval(function() {
      console.log('worker working');
    }, 10000);
    

    close

    var worker = new Worker('./worker');
    

    worker.js

    console.log('worker start');
    setInterval(function() {
      console.log('worker working');
    }, 1);
    // 5秒后终止线程
    setTimeout(function( {
      close();
    }))
    

    线程通信

    线程通信时通过 postMessage 函数发送数据, 通过 onmessage 事件监听数据. postMessage 接受一个参数, 这个参数会传递个指定的对应的线程, 对应线程的 onmessage 事件的处理函数接收一个参数 event, event.data 为 postMessage 函数发送的数据. 需要注意的是, 线程间通信的数据是不共享的, 通过 postMessage 发送的数据会被克隆, 生成一个副本. 数据的克隆使用结构化克隆算法(不同浏览器可能实现方式不一样), 所以有些数据无法拷贝, 例如函数. 当传入的数据包含无法克隆的数据类型是, 会抛出一个错误.

    main.js

    var worker = new Worker('./worker.js');
    // 监听 worker.js 中的 postMessage
    worker.onmessage = function(event) {
      console.log(event.data);
    }
    // 向 worker 发送一个消息
    worker.postMessage('message from main.js');
    

    worker.js

    // 接收发送给这个线程的数据
    onmessage = function(event) {
      console.log(event.data);
      // 接收的数据后发送一个 copy 消息
      postMessage('message from worker.js');
    }
    

    错误处理

    当 worker 线程跑出错误时, 会触发 onerror 事件, 可以使用 worker.onerror 事件处理 worker 线程的错误

    main.js

    var worker = new Worker('./worker');
    worker.onerror = function(error) {
      console.log('worker throw error: ', error);
    }
    

    worker.js

    throw new Error('worker throw error');
    close();
    

    引入脚本

    JS 中用 import 引入其他 JS 代码或其他文件, 在 worker 线程中引入其他 JS 代码使用 importScript 函数. importScript 函数可接收 0 个或多个 JS 文件 URL 作为参数.

    worker.js

    importScript('./log.js');
    log('import script');
    

    log.js

    function log(message) {
      console.log(message);
    }
    

    未完待续…

    展开全文
  • 使用vs2019创建Worker Service程序 首先nuget安装 Microsoft.Extensions.Hosting.Windows 在Program中添加UseWindowsService() public class Program { public static void Main(string[] args) { ...

    使用vs2019创建Worker Service程序
    首先nuget安装

    Microsoft.Extensions.Hosting.Windows

    在Program中添加UseWindowsService()

    public class Program
        {
            public static void Main(string[] args)
            {
                CreateHostBuilder(args).Build().Run();
            }
    
            public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args)
                    .ConfigureServices((hostContext, services) =>
                    {
                        services.AddHostedService<Worker>();
                    }).UseWindowsService();//部署windows值守服务
        }
    

    实际任务执行代码Worker类

    public class Worker : BackgroundService
        {
            private readonly ILogger<Worker> _logger;
    
            public Worker(ILogger<Worker> logger)
            {
                _logger = logger;
            }
    
            protected override async Task ExecuteAsync(CancellationToken stoppingToken)
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    //实际要执行的任务代码
                    await Task.Delay(8640000, stoppingToken);
                }
            }
        }
    

    发布程序后在程序目录用cmd执行以下代码

    sc.exe create 服务名称 binpath= xxxx.exe
    
    展开全文
  • A Lazy Worker

    2017-09-17 18:59:05
    There is a worker who may lack the motivation to perform at his peak level of efficiency because he is lazy. He wants to minimize the amount of work he does (he is Lazy, but he is subject to a ...
  • worker 141: error connecting to worker 83: Connection refused (os error 111); retrying worker 141: error connecting to worker 83: Connection refused (os error 111); retrying worker 141: error ...
  • (5)在主程序中定义N(=5)个Worker对象作为测试数据,完成对Worker类和程序的测试。 (6)在主程序中对其中某个工人发放奖金500元。 扩展1:在类Worker的基础上派生两个类:PieceWorker和HourWorker 在PieceWorker中...
  • ()">开始 Worker</button> ()">停止 Worker <br /> var w; function startWorker() { if(typeof(Worker)!=="undefined") { alert("w="+w); if(typeof(w)=="undefined") { w=new ...
  • master worker模式

    2014-07-14 22:31:19
    最近我在设计一套系统,初步想法是利用一个master来控制所有的worker(执行实际操作),通过配置可以增加或减少worker的数量,并且可以监测worker的存活性,但不使用master来为worker分配“工作”。 worker可以...
  • service worker介绍

    2017-05-02 12:21:13
    原文:Service workers explained 译者:neal1991 welcome to star my articles-translator , providing you advanced articles translation. Any suggestion, please issue or contact me ...

    原文:Service workers explained

    译者:neal1991

    welcome to star my articles-translator , providing you advanced articles translation. Any suggestion, please issue or contact me

    LICENSE: MIT

    那么它是什么?

    Service worker正是被开发用于解决web平台上经常出现的问题和疑虑,包括:

    • 无法解释(Extensible Web Manifesto 中)的HTTP缓存以及高级HTTP交互比如HTML5 AppCache。
    • 难以自然地构建一个离线优先地web应用。
    • 缺乏可以利用很多提出功能的上下文执行。

    我们也注意到了声明解决方案(Google Gears, Dojo Offline以及HTML5 AppCache都没能实现他们的承诺。每个连续的仅有声明的方法都以相同的方式失败了,所以service worker采取了一个不同的设计方法:一个可以用开发者牢牢把控的重要系统:

    Service worker就好像它的内部有一个有一个shared worker

    • 在它自己的全局脚本上下文中运行(通常是在它自己的线程中)
    • 不会和特定的页面绑定
    • 不能够访问DOM

    不像shared worker,它:

    • 即使没有页面也能够运行
    • 如果不使用的话可以终止,还可以再次运行当需要的时候(比如,他不是事件驱动的)
    • 拥有一个定义的升级模式
    • 只允许HTTPS(更多的是在这一点上)

    我们可以利用service workers:

    开始

    首先你需要注册一个service worker:

    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/my-app/sw.js').then(function(reg) {
        console.log('Yey!', reg);
      }).catch(function(err) {
        console.log('Boo!', err);
      });
    }

    在这个例子中,/my-app/sw.js就是service worker脚本的位置,并且它控制那些页面的URL以/my-app/开头。

    .register返回一个promise。如果你以前没接触过promise的话,可以看看HTML5Rocks article

    一些限制:

    • 注册页面必须安全地提供(没有证书错误的HTTPS)
    • service worker和页面必须同源,尽管你可使用 importScripts去导入其它地方的脚本

    • 作为必须的范围

    只有你说HTTPS?

    使用service worker,你可以劫持请求,进行不同的响应,并且过滤响应。这些功能都很强大。尽管你可以将这些能力用在好的地方,但是中间人可能不会。为了避免这一点,你只能在HTTPS上提供的页面上注册service worker,所以我们知道浏览器接收的service worker没有在网络种没有被篡改。

    Github Pages是由HTTPS提供服务的,所以是一个绝佳的展示demo的地方。

    初始生命周期

    当你调用.register之后,你的service worker会经历三个阶段

    1. Download
    2. Install
    3. Activate

    你可以使用事件和install以及activate进行交互:

    self.addEventListener('install', function(event) {
      event.waitUntil(
        fetchStuffAndInitDatabases()
      );
    });
    
    self.addEventListener('activate', function(event) {
      // You're good to go!
    });

    你可以向event.waitUntill传递一个promise从而来继承这个过程。一旦activate事件被触发了,你的service worker就可以控制页面了!

    那么我现在可以控制页面了?

    额,不完全是。当document浏览时,它会选择一个service worker作为它的控制器,因此你使用.register注册的document并不是被控制的,因为那并不是service worker首次加载的地方。

    如果你刷新document,它将会是在service worker的控制之下。你可以通过navigator.serviceWorker.controller来看一下是哪个service worker在进行控制,如果没有的话结果就会是null

    注意:当你从一个service worker更新到另外一个的时候,可能会有一点点不一点。我们会进入“Updating”阶段。

    如果使用shift来重载网页的话,加载就会有控制器了,这样做是为了测试CSS以及JS变化。

    Document通常是和一个service worker存在于整个声明周期,或者根本就没有service worker。然而,service worker可以调用self.skipWaiting()(spec) 来立刻接管范围内的所有页面。

    网络截获

    self.addEventListener('fetch', function(event) {
      console.log(event.request);
    });

    你可以利用fetch事件:

    • 在你的service worker作用域下浏览
    • 任何被这些页面触发的请求,甚至是对其他源的请求

    这意味着你可以监听所有对于这个页面的请求,CSS,JS,图片,XHR,图标等等所有。

    • iframes & <object>–这些将根据它们的资源URL选择其控制器
    • Service workers - 对于service worker的fetch/update请求不会通过service worker
    • 请求是在service worker之内出发的 - 否则你会获得一个循环

    request对象会给你关于这个request的信息,比如它的URL,方法以及头部。但是最有趣的是,他可以劫持请求并且给出不同的响应。

    self.addEventListener('fetch', function(event) {
      event.respondWith(new Response("Hello world!"));
    });

    这是一个 demo.

    .repondWith使用一个Reponse对象或者一个解析后的promise。上面我们是在创建一个手工的response。这个Reponse对象来自于 Fetch Spec.。在这个规范里面同样也存在着fetch()方法,它会返回一个promise作为响应,这意味着你可以在任何地方获取你的响应。

    self.addEventListener('fetch', function(event) {
      if (/\.jpg$/.test(event.request.url)) {
        event.respondWith(
          fetch('//www.google.co.uk/logos/…3-hp.gif', {
            mode: 'no-cors'
          })
        );
      }
    });

    在上面,我捕获了以.jpg结尾的请求并且将Google doodle作为响应。fetch()请求默认是 CORS,但是通过设置no-cors我可用使用这个响应,即使他不能跨域访问headers(尽管我们不能利用JavaScript访问内容)。这是demo.

    Promise能够让你从一个方法返回到另外一个方法:

    self.addEventListener('fetch', function(event) {
      event.respondWith(
        fetch(event.request).catch(function() {
          return new Response("Request failed!");
        })
      );
    });

    Service worker是带有一个cache API,使得以后可以方便的存储响应以便重用。不久之后,但是第一点

    更新一个service worker

    Service worker的生命周期是建立在Chrome的更新模型上的:在后台尽可能多地做,不要打扰用户,当当前版本关闭的时候完成更新。

    无论何时你在你的service worker作用域内浏览页面,浏览器都会在后台检查更新。如果这个脚本是字节不同的,那么它就会被认为是一个新的版本,并且被安装(注意:只有这个脚本被检查,而不是外部的importScripts)。然而,老版本的会持续对页面的控制直到所有使用它的tab都被关闭了(除非在install的过程中调用.replace())。接着这个老版本的就会被回收从而新的版本开始接管。

    这样做是为了避免同时运行两个版本的service worker在不同的tab中。我们当前的策略是: “cross fingers, hope it doesn’t happen”.

    注意:更新遵顼header中worker脚本的新鲜度(比如max-age),除非max-age大于24个小时,否则最多只能保持24个小时。

    self.addEventListener('install', function(event) {
      // this happens while the old version is still in control
      event.waitUntil(
        fetchStuffAndInitDatabases()
      );
    });
    
    self.addEventListener('activate', function(event) {
      // the old version is gone now, do what you couldn't
      // do while it was still around
      event.waitUntil(
        schemaMigrationAndCleanup()
      )
    });

    下面是实践中的实现

    不幸的是,刷新一个tab不足够收集到旧的woker兵器让新的进行接管。浏览期在上传当前页面之前向下一个页面发送请求,所以不存在当前active worker被释放。

    最简单的方法是关闭然后重新打开这个tab(cmd+w,然后cmd+shift+t Mac),或者shift+reload然后就是正常的重新加载了。

    缓存

    Service worker带有一个caching API能够让你产生由请求作为键值的store。

    self.addEventListener('install', function(event) {
      // pre cache a load of stuff:
      event.waitUntil(
        caches.open('myapp-static-v1').then(function(cache) {
          return cache.addAll([
            '/',
            '/styles/all.css',
            '/styles/imgs/bg.png',
            '/scripts/all.js'
          ]);
        })
      )
    });
    
    self.addEventListener('fetch', function(event) {
      event.respondWith(
        caches.match(event.request).then(function(cachedResponse) {
          return cachedResponse || fetch(event.request);
        })
      );
    });

    在缓存之内匹配类似于浏览器的缓存。方法,URL以及varyheader都被考虑在内,但是header的新鲜度被忽略了。缓存的东西只有在你手动移除的时候才生效。

    你可以通过cache.put(request, response)向缓存中添加独立的条目,包括你自己产生的。你也可以控制匹配,忽略其它的,比如查询字符串,方法以及vary header。

    其它service worker相关的标准

    由于service worker可以及时地调动事件,及时未打开页面,也可以在后台偶尔调用其它功能:

    总结

    这份文档只是简要地介绍了service worker的能力,并不是售空页面或者service worker实例的所有的可用的API。也不涉及创作,修改以及更新应用程序的service worker。通过这个,希望能够引导你理解service worker中的promise以及对于URL友好的以及可伸缩的默认支持离线使用的web应用的丰富的promise。

    Acknowledgments

    Many thanks to Web Personality of the Year nominee Jake (“B.J.”) Archibald, David Barrett-Kahn, Anne van Kesteren, Michael Nordman, Darin Fisher, Alec Flett, Andrew Betts, Chris Wilson, Aaron Boodman, Dave Herman, Jonas Sicking, Greg Billock, Karol Klepacki, Dan Dascalescu, and Christian Liebel for their comments and contributions to this document and to the discussions that have informed it.

    展开全文
  • Worker Thread 模式

    2012-02-04 14:34:18
    每個執行緒處理一個請求,每次執行緒執行完請求後,再次嘗試取得下一個請求並執行,這是Worker Thread的基本概念,對於一些需要冗長計算或要在背景執行的請求,可以採用Worker Thread。 在 Thread-Per-Message ...
    每個執行緒處理一個請求,每次執行緒執行完請求後,再次嘗試取得下一個請求並執行,這是Worker Thread的基本概念,對於一些需要冗長計算或要在背景執行的請求,可以採用Worker Thread。

    在 
    Thread-Per-Message 模式 中,其實已經有點Worker Thread的概念,在Service物件接收到資料後,以匿名方式建立執行緒來處理資料,那個建立的執行緒就是Worker Thread,只不用過就丟了。

    Worker Thread可以應用在不同的場合,例如在 
    Guarded Suspension 模式 的範例,是使用一個執行緒來處理請求佇列中的請求,如果請求不斷來到,且請求中可能有冗長的處理,則請求佇列中的請求可能會來不及消化。

    您可以為請求佇列中的每個請求配給一個執行緒來處理,不過實際上,只要建立足夠多的執行緒即可,在以下的範例中,可以指定請求佇列預先建立的執行緒數量,每個執行緒會取出一個請求來執行。
    import java.util.*;
    
    interface Request {
        void execute();
    }
    
    class Worker implements Runnable {
        private RequestQueue queue;
        Worker(RequestQueue queue) {
            this.queue = queue;
        }
        public void run() {
            while(true) {
                queue.get().execute();
            }
        }
    }
    
    class RequestQueue {
        private LinkedList<Request> requests;
        
        RequestQueue(int workers) {
            requests = new LinkedList<Request>();
            for(int i = 0; i < workers; i++) {
                (new Thread(new Worker(this))).start();
            }
        }
    
        synchronized Request get() {
            while(requests.size() == 0) {
                try {
                    wait();
                }
                catch(InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return requests.removeFirst();
        }
    
        synchronized void put(Request request) {
            requests.addLast(request);
            notifyAll();
        }
    } 
    
    // 模擬 Client 置入請求
    class Client implements Runnable {
        private RequestQueue queue;
        Client(RequestQueue queue) {
            this.queue = queue;
        }
        public void run() {
            while(true) {
                Request request = new Request() {
                    public void execute() {
                       System.out.println("執行客戶請求...XD");
                       try {
                           Thread.sleep((int) (Math.random() * 3000)); 
                       }
                       catch(InterruptedException e) {
                           e.printStackTrace();
                       }
                    }
                };
                queue.put(request);
                try {
                    Thread.sleep((int) (Math.random() * 3000)); 
                }
                catch(InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            RequestQueue queue = new RequestQueue(5);
            for(int i = 0; i < 5; i++) {
                (new Thread(new Client(queue))).start();
            }    
        }
    }

    在這個範例中,
     Worker Thread有請求來了就作,如果沒有請求,則所有的Worker Thread就等待,直到有新的工作進來而通知它們,取得請 求的WorkerThread要作的工作,就直接定義在execute()中。 

    以順序圖來表示這個範例:

    若使用Python來示範的:
    import threading
    import time
    import random
    
    class Worker(threading.Thread):
        def __init__(self, queue):
            threading.Thread.__init__(self)
            self.queue = queue
        
        def run(self):
            while True:
                self.queue.get()()
            
    class RequestQueue:
        def __init__(self, workers):
            self.requests = []
            self.condition = threading.Condition()
            for i in range(workers):
                Worker(self).start()
    
        def get(self):
            self.condition.acquire()
            while not self.requests:
                self.condition.wait()
            request = self.requests.pop(0)
            self.condition.release()
            return request
       
        def put(self, request):
            self.condition.acquire()
            self.requests.append(request)
            self.condition.notify()
            self.condition.release()
    
    class Client(threading.Thread):
        def __init__(self, queue):
            threading.Thread.__init__(self)
            self.queue = queue
            
        def run(self):
            while True:
                second = int(random.random() * 3) # 隨機模擬請求的執行時間
                request = lambda: print("執行客戶請求...XD"); time.sleep(second)
                self.queue.put(request)
                time.sleep(int(random.random() * 3))
                
    queue = RequestQueue(5)
    for i in range(5):
        Client(queue).start()
            
    while True:
        try:
            time.sleep(1)
        except KeyboardInterrupt:
            exit()
    展开全文
  • WebWorker 是什么? 为 JavaScript 引入线程技术 不必再用 setTimeout()、setInterval()、XMLHttpRequest 来模拟并行 Worker 利用类似线程的消息传递实现并行。这非常适合您确保对 UI 的刷新、性能以及对用户的响应...
  • 前端worker之web worker

    2019-10-03 23:55:11
    web worker 背景 众所周知javascript是单线程的,同一时间内只能做一件事情。 这是十分必要的,设想,如果js是多线程的。有个dom元素两个线程同时做了改变,一个display:none,另一个display:block,这样让浏览器就...
  • Service Worker初体验

    千次阅读 2017-02-15 23:29:37
    概述在讲Service Worker之前先说一下另一个概念:PWA(Progressive Web Apps) Progressive Web App:是一个具有响应式布局的Web应用,可以离线工作,并能够安装到设备的主屏幕上。其实是在主屏幕上添加该Web应用的...
  • Web Worker 使用指南

    2020-02-20 23:31:16
    Web Worker 是 HTML5 新出的标准。...在 JavaScript 中,主线程可以创建多个 Worker 子线程。Worker 子线程不影响主线程的执行,也就是不影响页面交互,只在后台运行,等到计算完成,再把结果返回给主线程。
  • Web Worker Best Practices

    千次阅读 2016-07-20 14:20:53
    使用Web Worker可以把一些比较计算量相对大的阻塞浏览器响应的计算放在单独的线程里计算。请求优化构造Worker的时候需要给定js的链接URL,worker内部请求js运行代码。假如worker有若干个,但使用同一个js文件,也...
  • HTML5 Web Worker

    2018-02-05 16:36:40
    var worker=new Worker("js/worker.js"); //创建一个Worker对象,并向它传递将在新线程中; worker.postMessage("hello world SB"); //向worker发送数据 worker.onmessage=function...
  • Storm通信机制Worker间的通信经常需要通过网络跨节点进行,Storm使用ZeroMQ或Netty(0.9以后默认使用)作为进程间通信的消息框架。 Worker进程内部通信:不同worker的thread通信使用LMAX Disruptor来完成。 不同...
  • v8worker

    千次阅读 2019-05-08 11:54:53
  • worker role 频繁重启

    2016-03-17 03:40:17
    我的worker role在遇到系统事件的restartManager(见图片![图片说明](https://img-ask.csdn.net/upload/201603/17/1458185985_752160.png))后就会重新启动,这个时候我的role没有 执行任何任务,而且重启很平凡,...
  • 从webWorker到serviceWorker

    2015-12-09 11:23:00
    2019独角兽企业重金招聘Python工程师标准>>> ...
  • Web Worker

    2015-12-11 23:00:18
    web worker 是运行在后台的js,独立与其他脚本,不会影响页面的性能。web worker 是使用多线程,不会影响主线程的运行。声明(启动)一个worker: var w = new Worker('task.js'); worker的三个方法: post...
  • worker_processes与worker_connections 设置好合适大小,可以提示nginx处理性能,非常重要。 原作者的话: As a general rule you need the only worker with large number of worker_connections, say 10,000 or ...
  • ServiceWorker

    千次阅读 2017-08-15 21:38:22
    什么是ServiceWorker websocket是H5的新API,是一段运行在浏览器后台的JavaScript脚本,独立于当前页面,因此不能和网页交互,它可以拦截和处理网络请求,实现缓存资源并在离线时响应用户的请求。 ServiceWorker...
  • kthread worker

    2018-12-01 16:43:52
    kthread worker 起始就是kernel 帮用户开创建内核线程,简化复杂度。 主要的函数有三个,其使用举例如下: 创建一个worker: pool-&gt;worker = kthread_create_worker(0, "ib_fmr(%s)", device-&...
  • worker 理解

    2019-07-11 17:10:54
    JavaScript为单线程,worker则为JavaScript创建多线程环境。 使用场景如:计算文件hash,计算大于1G的文件hash过程是很慢的,但由于要将hash传给后端,所有需要在计算完成以后,再请求后端接口 fast_upload,请求 ...
  • web worker

    2017-04-27 10:42:56
    什么是Web Workers?为什么我们需要他们?... 如果你能移动这些繁重的循环到Javascript文件中,采用异步的方式运行,这意味着浏览器不需要等到循环接触,我们可以有更敏感的浏览器,这就是web worker的作用。Web work

空空如也

1 2 3 4 5 ... 20
收藏数 51,756
精华内容 20,702
关键字:

worker