threads

October 22, 2022

threads

Stability: 1-Experiment

The threads module provides multi-threading support and can start a new thread to run scripts.

The main thread of the script will wait for the execution of all child threads to complete before stopping. Therefore, if there is an infinite loop in the child threads, please call exit() to stop the script directly or threads.shutDownAll() to stop all if necessary. Child thread.

All threads started by threads.start() will automatically stop when the script is forcibly stopped.

Because JavaScript does not have multithreading support, you may encounter unexpected problems.

threads.start(action)

  • action {Function} The function to be executed in the new thread
  • Return Thread

Start a new thread and execute the action.

E.g:

threads.start(function(){
    //Code executed in a new thread
    while(true){
        log("child thread");
        sleep(1000);
        }
});
while(true){
    log("Script main thread");
    sleep(1000);
    }

The Thread object returned by this function can obtain the status of the thread and control the running of the thread. E.g:

var thread = threads.start(function(){
    while(true){
        log("child thread");
        sleep(1000);
        }
});
//Stop thread execution
thread.interrupt();

See Thread for more information.

threads.shutDownAll()

Stop all child threads started by threads.start().

threads.currentThread()

Return the current thread.

threads.disposable()

Create a Disposable object to wait for a one-time result from another thread. For more information, see [Thread Communication](#threads_Thread Communication) and Disposable.

threads.atomic([initialValue])

Create a new integer atomic variable. For more information, see [Thread Safety](#threads_Thread Safety) and AtomicLongopen in new window .

threads.lock()

Create a reentrant lock. For more information, see [Thread Safety](#threads_Thread Safety) and ReentrantLockopen in new window .

Thread

The thread object, the object returned by threads.start(), is used to obtain and control the state of a thread, interact with other threads, and so on.

The Thread object provides the same API as the timers module, such as setTimeout(), setInterval(), etc., which are used to execute the corresponding timing callbacks in the thread, so that the threads can directly interact. E.g:

var thread = threads.start(function(){
    //Timer executed in the child thread
    setInterval(function(){
        log("Child thread:" + threads.currentThread());
    }, 1000);
});

log("The current thread is the main thread:" + threads.currentThread());

//Wait for the child thread to start
thread.waitFor();
//Timer executed in the child thread
thread.setTimeout(function(){
    //This code will be executed in the child thread
    log("The current thread is a child thread:" + threads.currentThread());
}, 2000);

sleep(30 * 1000);
thread.interrupt();

Thread.interrupt()

Interrupt the thread running.

Thread.join([timeout])

  • timeout {number} waiting time in milliseconds

Wait for the thread execution to complete. If timeout is 0, it will wait until the thread execution is completed; otherwise, it will wait at most timeout milliseconds.

E.g:

var sum = 0;
//Start the child thread to calculate 1 and add to 10000
var thread = threads.start(function(){
    for(var i = 0; i <10000; i++){
        sum += i;
    }
});
//Wait for the thread to complete
thread.join();
toastLog("sum = "+ sum);

isAlive()

  • Return {boolean}

Returns whether the thread is alive. If the thread has not started or has ended, return false; if the thread has started or is running, return true.

waitFor()

Wait for the thread to start executing. After calling threads.start(), the thread still needs some time to start execution, so calling this function will wait for the thread to start execution; if the thread is already in the execution state, it will return immediately.

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

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

See timers.setTimeout().

The difference is that the timer will be executed in this thread. If the current thread has not yet started execution or has finished execution, an ʻIllegalStateException` is thrown.

log("current thread (main thread):" + threads.currentThread());

var thread = threads.start(function(){
    //Set an empty timing to keep the thread running
    setInterval(function(){}, 1000);
});

sleep(1000);
thread.setTimeout(function(){
    log("current thread (child thread):" + threads.currentThread());
    exit();
}, 1000);

Thread.setInterval(callback, delay[, ...args])

See timers.setInterval().

The difference is that the timer will be executed in this thread. If the current thread has not yet started execution or has finished execution, an ʻIllegalStateException` is thrown.

Thread.setImmediate(callback[, ...args])

See timers.setImmediate().

The difference is that the timer will be executed in this thread. If the current thread has not yet started execution or has finished execution, an ʻIllegalStateException` is thrown.

Thread.clearInterval(id)

See timers.clearInterval().

The difference is that the timer will be executed in this thread. If the current thread has not yet started execution or has finished execution, an ʻIllegalStateException` is thrown.

Thread.clearTimeout(id)

See timers.clearTimeout().

The difference is that the timer will be executed in this thread. If the current thread has not yet started execution or has finished execution, an ʻIllegalStateException` is thrown.

Thread.clearImmediate(id)

See timers.clearImmediate().

The difference is that the timer will be executed in this thread. If the current thread has not yet started execution or has finished execution, an ʻIllegalStateException` is thrown.

Thread safety

Thread safety is a relatively professional programming problem, and this chapter is only provided to users who need it.

Quoting Wikipedia's explanation:

Thread safety is a term in programming, which means that when a function or function library is called in a multithreaded environment, it can correctly handle the shared variables between multiple threads, so that the program functions can be completed correctly.

In CloudControl, inter-thread variables are shared under the premise of complying with JavaScript variable scope rules. For example, global variables can be accessed in all threads, and their visibility in all threads is guaranteed. However, the atomicity of any operation is not guaranteed. For example, the classic self-increment "i++" will not be an atomic operation.

Rhino and CloudControl Provide some simple facilities to solve simple thread safety problems, such as lock threads.lock(), function synchronization lock sync(), integer atomic variable threads.atomic(), etc.

For example, for the self-increment operation of integers shared by multiple threads (the self-increment operation can cause problems because the self-increment operation is actually i = i + 1, that is, read the value of i first, and add 1 to it , And then assign a value to i, if two threads perform auto-increment operations at the same time, the value of i may only increase by 1), you should use the threads.atomic() function to create an integer atomic variable, or use a lock threads.lock() to ensure the atomicity of the operation, or use sync() to increase synchronization locks.

The thread-unsafe code is as follows:

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

After running this piece of code, open the log, and you can see that there are duplicate values ​​in the log.

The thread-safe code using threads.atomic() is as follows:

//The object returned by atomic guarantees the atomicity of self-increment
var i = threads.atomic();
threads.start(function(){
    while(true){log(i.getAndIncrement());
    sleep(1000);
    }
});
while(true){
    log(i.getAndIncrement());
    sleep(1000);
    }

or:

//The lock guarantees the atomicity of the operation
var lock = threads.lock();
var i = 0;
threads.start(function(){
    while(true){
        lock.lock();
        log(i++);
        lock.unlock();
        sleep(1000);
        }
});
while(true){
    lock.lock();
    log(i++);
    lock.unlock();
    sleep(1000);
    }

or:

//The sync function adds a synchronization lock to the function inside, so that at most one thread can execute this function at the same time
var i = 0;
var getAndIncrement = sync(function(){
    return i++;
});
threads.start(function(){
    while(true){
        log(getAndIncrement());
        sleep(1000);
        }
});
while(true){
    log(getAndIncrement());
    sleep(1000);
    }

In addition, Array is not thread-safe. If you have such a complex requirement, please use Android and Java related APIs to implement it. For example, CopyOnWriteList, Vector, etc. are all thread-safe classes that replace arrays and are used in different scenarios. E.g:

var nums = new java.util.Vector();
nums.add(123);
nums.add(456);
toastLog("length is" + nums.size());
toastLog("The first element is" + nums.get(0));

But it is obvious that these classes are not as easy to use as arrays, nor can you use convenient functions such as slice(). In the future, thread-safe arrays may be added to solve this problem. Of course, you can also lock each array operation to solve thread safety issues:

var nums = [];
var numsLock = threads.lock();
threads.start(function(){
    //Add element 123 to the array
    numsLock.lock();
    nums.push(123);
    log("Thread: %s, Array: %s", threads.currentThread(), nums);
    numsLock.unlock();
});

threads.start(function(){
    //Add element 456 to the array
    numsLock.lock();
    nums.push(456);
    log("Thread: %s, Array: %s", threads.currentThread(), nums);
    numsLock.unlock();
});

//Delete the last element of the array
numsLock.lock();
nums.pop();
log("Thread: %s, Array: %s", threads.currentThread(), nums);
numsLock.unlock();

sync(func)

  • func {Function} function
  • Return {Function}

Add a synchronization lock to the function func and return as a new function.

var i = 0;
function add(x){
    i += x;
}

var syncAdd = sync(add);
syncAdd(10);
toastLog(i);

Thread communication

CloudControl Provides some simple facilities to support simple thread communication. threads.disposable() is used for one thread to wait for the (one-time) result of another thread, while Lock.newCondition() provides a Condition object for general thread communication (await, signal). In addition, the events module can also be used for thread communication, which is achieved by specifying the thread of the callback execution of the EventEmitter.

Use threads.disposable() to simply wait and get the execution result of a thread. For example, to wait for a thread to calculate "1+...+10000":

var sum = threads.disposable();
//Start sub-thread calculation
threads.start(function(){
    var s = 0;
    //Add from 1 to 10000
    for(var i = 1; i <= 10000; i++){
        s += i;
    }
    //Notify the main thread to receive the result
    sum.setAndNotify(s);
});
//blockedGet() is used to wait for the result
toastLog("sum = "+ sum.blockedGet());

If the above code is implemented with Condition:

//Create a new lock
var lock = threads.lock();
//Create a new condition, namely "calculation completed"
var complete = lock.newCondition();
var sum = 0;
threads.start(function(){
    //Add from 1 to 10000
    for(var i = 1; i <= 10000; i++){
        sum += i;
    }
    //Notify the main thread to receive the result
    lock.lock();
    complete.signal();
    lock.unlock();
});
//Wait for the calculation to complete
lock.lock();
complete.await();
lock.unlock();
//Print the result
toastLog("sum = "+ sum);

If the appeal code is implemented with the events module:

//Create a new emitter, and specify the thread of callback execution as the current thread
var sum = events.emitter(threads.currentThread());
threads.start(function(){
    var s = 0;
    //Add from 1 to 10000
    for(var i = 1; i <= 10000; i++){
        s += i;
    }
    //Send event result to notify the main thread to receive the result
    sum.emit('result', s);
});
sum.on('result', function(s){
    toastLog("sum = "+ s + ", current thread:" + threads.currentThread());
});

For other issues related to threads, such as producer and consumer issues, please use Java-related methods to solve them, such as java.util.concurrent.BlockingQueue.

Last update:
Contributors: Bruce