Auto.js 4.1.0 文档

索引 |

目录

  • Threads#

    #
    • action <Function> 要在新线程执行的函数
    • 返回 Thread对象可以获取该线程的状态,控制该线程的运行中。例如:

      var thread = threads.start(function(){
          while(true){
              log("子线程");
          }
      });
      //停止线程执行
      thread.interrupt();
      

      更多信息参见#

      停止所有通过threads.start()启动的子线程。

      threads.currentThread()#

      • 返回 #
        • 返回 线程通信以及#

          新建一个整数原子变量。更多信息参见AtomicLong

          threads.lock()#

          新建一个可重入锁。更多信息参见ReentrantLock

          Thread#

          线程对象,threads.start()返回的对象,用于获取和控制线程的状态,与其他线程交互等。

          Thread对象提供了和timers模块一样的API,例如setTimeout(), setInterval()等,用于在该线程执行相应的定时回调,从而使线程之间可以直接交互。例如:

          var thread = threads.start(function(){
              //在子线程执行的定时器
              setInterval(function(){
                  log("子线程:" + threads.currentThread());
              }, 1000);
          });
          
          log("当前线程为主线程:" + threads.currentThread());
          
          //等待子线程启动
          thread.waitFor();
          //在子线程执行的定时器
          thread.setTimeout(function(){
              //这段代码会在子线程执行
              log("当前线程为子线程:" + threads.currentThread());
          }, 2000);
          
          sleep(30 * 1000);
          thread.interrupt();
          

          Thread.interrupt()#

          中断线程运行。

          Thread.join([timeout])#

          • timeout <number> 等待时间,单位毫秒

          等待线程执行完成。如果timeout为0,则会一直等待直至该线程执行完成;否则最多等待timeout毫秒的时间。

          例如:

          var sum = 0;
          //启动子线程计算1加到10000
          var thread = threads.start(function(){
              for(var i = 0; i < 10000; i++){
                  sum += i;
              }
          });
          //等待该线程完成
          thread.join();
          toast("sum = " + sum);
          

          isAlive()#

          返回线程是否存活。如果线程仍未开始或已经结束,返回false; 如果线程已经开始或者正在运行中,返回true

          waitFor()#

          等待线程开始执行。调用threads.start()以后线程仍然需要一定时间才能开始执行,因此调用此函数会等待线程开始执行;如果线程已经处于执行状态则立即返回。

          var thread = threads.start(function(){
              //do something
          });
          thread.waitFor();
          thread.setTimeout(function(){
              //do something
          }, 1000);
          

          Thread.setTimeout(callback, delay[, ...args])#

          参见#

          参见#

          参见#

          参见#

          参见#

          参见#

          线程安全问题是一个相对专业的编程问题,本章节只提供给有需要的用户。

          引用维基百科的解释:

          线程安全是编程中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。

          在Auto.js中,线程间变量在符合JavaScript变量作用域规则的前提下是共享的,例如全局变量在所有线程都能访问,并且保证他们在所有线程的可见性。但是,不保证任何操作的原子性。例如经典的自增"i++"将不是原子性操作。

          Rhino和Auto.js提供了一些简单的设施来解决简单的线程安全问题,如锁threads.lock(), 函数同步锁sync(), 整数原子变量threads.atomic()等。

          例如,对于多线程共享下的整数的自增操作(自增操作会导致问题,是因为自增操作实际上为i = i + 1,也就是先读取i的值, 把他加1, 再赋值给i, 如果两个线程同时进行自增操作,可能出现i的值只增加了1的情况),应该使用threads.atomic()函数来新建一个整数原子变量,或者使用锁threads.lock()来保证操作的原子性,或者用sync()来增加同步锁。

          线程不安全的代码如下:

          var i = 0;
          threads.start(function(){
              while(true){
                  log(i++);
              }
          });
          while(true){
              log(i++);
          }
          

          此段代码运行后打开日志,可以看到日志中有重复的值出现。

          使用threads.atomic()的线程安全的代码如下:

          //atomic返回的对象保证了自增的原子性
          var i = threads.atomic();
          threads.start(function(){
              while(true){
                  log(i.getAndIncrement());
              }
          });
          while(true){
              log(i.getAndIncrement());
          }
          

          或者:

          //锁保证了操作的原子性
          var lock = threads.lock();
          var i = 0;
          threads.start(function(){
              while(true){
                  lock.lock();
                  log(i++);
                  lock.unlock();
              }
          });
          while(true){
              lock.lock();
              log(i++);
              lock.unlock();
          }
          

          或者:

          //sync函数会把里面的函数加上同步锁,使得在同一时刻最多只能有一个线程执行这个函数
          var i = 0;
          var getAndIncrement = sync(function(){
              return i++;
          });
          threads.start(function(){
              while(true){
                  log(getAndIncrement());
              }
          });
          while(true){
              log(getAndIncrement());
          }
          

          另外,数组Array不是线程安全的,如果有这种复杂的需求,请用Android和Java相关API来实现。例如CopyOnWriteList, Vector等都是代替数组的线程安全的类,用于不同的场景。例如:

          var nums = new java.util.Vector();
          nums.add(123);
          nums.add(456);
          toast("长度为" + nums.size());
          toast("第一个元素为" + nums.get(0));
          

          但很明显的是,这些类不像数组那样简便易用,也不能使用诸如slice()之类的方便的函数。在未来可能会加入线程安全的数组来解决这个问题。当然您也可以为每个数组的操作加锁来解决线程安全问题:

          var nums = [];
          var numsLock = threads.lock();
          threads.start(function(){
              //向数组添加元素123
              numsLock.lock();
              nums.push(123);
              log("线程: %s, 数组: %s", threads.currentThread(), nums);
              numsLock.unlock();
          });
          
          threads.start(function(){
              //向数组添加元素456
              numsLock.lock();
              nums.push(456);
              log("线程: %s, 数组: %s", threads.currentThread(), nums);
              numsLock.unlock();
          });
          
          //删除数组最后一个元素
          numsLock.lock();
          nums.pop();
          log("线程: %s, 数组: %s", threads.currentThread(), nums);
          numsLock.unlock();
          

          sync(func)#

          给函数func加上同步锁并作为一个新函数返回。

          var i = 0;
          function add(x){
              i += x;
          }
          
          var syncAdd = sync(add);
          syncAdd(10);
          toast(i);
          

          线程通信#

          Auto.js提供了一些简单的设施来支持简单的线程通信。threads.disposable()用于一个线程等待另一个线程的(一次性)结果,同时Lock.newCondition()提供了Condition对象用于一般的线程通信(await, signal)。另外,events模块也可以用于线程通信,通过指定EventEmiiter的回调执行的线程来实现。

          使用threads.disposable()可以简单地等待和获取某个线程的执行结果。例如要等待某个线程计算"1+.....+10000":

          var sum = threads.disposable();
          //启动子线程计算
          threads.start(function(){
              var s = 0;
              //从1加到10000
              for(var i = 1; i <= 10000; i++){
                  s += i;
              }
              //通知主线程接收结果
              sum.setAndNotify(s);
          });
          //blockedGet()用于等待结果
          toast("sum = " + sum.blockedGet());
          

          如果上述代码用Condition实现:

          //新建一个锁
          var lock = threads.lock();
          //新建一个条件,即"计算完成"
          var complete = lock.newCondition();
          var sum = 0;
          threads.start(function(){
              //从1加到10000
              for(var i = 1; i <= 10000; i++){
                  sum += i;
              }
              //通知主线程接收结果
              lock.lock();
              complete.signal();
              lock.unlock();
          });
          //等待计算完成
          lock.lock();
          complete.await();
          lock.unlock();
          //打印结果
          toast("sum = " + sum);
          

          如果上诉代码用events模块实现:

          //新建一个emitter, 并指定回调执行的线程为当前线程
          var sum = events.emitter(threads.currentThread());
          threads.start(function(){
              var s = 0;
              //从1加到10000
              for(var i = 1; i <= 10000; i++){
                  s += i;
              }
              //发送事件result通知主线程接收结果
              sum.emit('result', s);
          });
          sum.on('result', function(s){
              toastLog("sum = " + s + ", 当前线程: " + threads.currentThread());
          });
          

          有关线程的其他问题,例如生产者消费者等问题,请用Java相关方法解决,例如java.util.concurrent.BlockingQueue