尝试为递归函数实现内联 Webworker

Trying to implement inline Webworker for a recursive function

我有一个使用递归函数的第一个版本 Javascript,它产生了预期的结果。低于工作版本:

// Call the recursive function and get final (a,b) results
var HitTemp = JSON.parse(JSON.stringify(HitCurrent));
var result= recursiveFunction(HitTemp, HitTemp.playerCurrent, maxNodes);
var a = HitTemp.coordPlayable[0];
var b = HitTemp.coordPlayable[1];

// Recursive function
function recursiveFunction(HitCurrent, colorCurrent, depth) {
 // Indices
 var i, j, k;
 // Evaluation
 var arrayTemp, eval, e;
 // Set current color to HitCurrent
 HitCurrent.playerCurrent = colorCurrent;
 // Deep copy of arrayCurrent array
 arrayTemp = JSON.parse(JSON.stringify(HitCurrent.arrayCurrent));
 // If depth equal to 0
 if (depth == 0)
  return evaluation(HitCurrent);
 // Starting evaluation
 eval = -infinity;
 // Browse all possible hits
 for (i = 0; i < 8; i++)
  for (j = 0; j < 8; j++) {
   if (HitCurrent.arrayPlayable[i][j] == 'playable') {
    for (k = 0; k < 8; k++) {
     // Explore line started from (i,j) with direction "k"
     exploreHitLine(HitCurrent, i, j, k, 'drawing');
    }
    // Recursive call
    e = recursiveFunction(JSON.parse(JSON.stringify(HitCurrent)), ((JSON.stringify(HitCurrent.playerCurrent) == JSON.stringify(playerBlack)) ? playerWhite : playerBlack), depth-1);
    if (e > eval) {
     HitCurrent.coordPlayable = [i,j];
     eval = e;
    }
   }
   // Restore arrayCurrent array
   HitCurrent.arrayCurrent = JSON.parse(JSON.stringify(arrayTemp));
   }
 return eval;
}

据此,我想使用"inline" WebWorkers,将递归专用于WebWorker,避免在浏览器中挂起进程。

我试着关注this link and this other link

我不知道我是否必须将对象 HitCurrent 或值 evalpostmessage”到主线程:通过使用 WebWorker,我在return 指令(returns 终端情况下的一个值)和为下一次递归调用传递的对象 HitCurrent 参数。

如果有人可以提供一些线索来使用内联 webworker(或使用 webworker 的经典方式)重现这个原始算法。

内联网络工作者示例:

作为你的代码,没有function evaluationfunction exploreHitLine。 在使用以下代码之前,您必须将它们插入 code.

{
    let workerScript = URL.createObjectURL( new Blob( [ `
    "use strict";
    // Recursive function
    function recursiveFunction( HitCurrent, colorCurrent, depth ) {
        // Indices
        var i, j, k;
        // Evaluation
        var arrayTemp, eval, e;
        // Set current color to HitCurrent
        HitCurrent.playerCurrent = colorCurrent;
        // Deep copy of arrayCurrent array
        arrayTemp = JSON.parse(JSON.stringify(HitCurrent.arrayCurrent));
        // If depth equal to 0
        if ( depth === 0 ) return evaluation(HitCurrent);
        // Starting evaluation
        eval = -infinity;
        // Browse all possible hits
        for (i = 0; i < 8; i++) {
            for (j = 0; j < 8; j++) {
                if (HitCurrent.arrayPlayable[i][j] === 'playable') {
                    for (k = 0; k < 8; k++) {
                        // Explore line started from (i,j) with direction "k"
                        exploreHitLine(HitCurrent, i, j, k, 'drawing');
                    }
                    // Recursive call
                    e = recursiveFunction(JSON.parse(JSON.stringify(HitCurrent)), ((JSON.stringify(HitCurrent.playerCurrent) == JSON.stringify(playerBlack)) ? playerWhite : playerBlack), depth-1);
                    if (e > eval) {
                        HitCurrent.coordPlayable = [i,j];
                        eval = e;
                    }
                }
                // Restore arrayCurrent array
                HitCurrent.arrayCurrent = JSON.parse(JSON.stringify(arrayTemp));
            }
        }
        return eval;
    }
    onmessage = function ( event ) {
        let params = event.data;
        postMessage( { result: recursiveFunction( ...params ) } );
    }
        ` ], { type: "plain/text" } ) );


    // Call the recursive function and get final (a,b) results
    new Promise( resolve => {
        let HitTemp = JSON.parse(JSON.stringify(HitCurrent));
        let firstWorker = new Worker( workerScript );
        firstWorker.onmessage = function ( event ) {
            resolve( event.data ); //{ result: XXX }
        }
        firstWorker.postMessage( HitTemp, HitTemp.playerCurrent, maxNodes );
    } ).then( ( { result } ) => {
        let [ a, b ] = result.coordPlayable;
        console.log( result );
    } );
}

另外以下正在运行的内联 WebWorker:

{
    let workerScript = URL.createObjectURL( new Blob( [ `
    "use strict";
    onmessage = function ( event ) {
        let sum = 0, count = event.data;
        for ( let i = 0; i < count**count; i++ ) {
            sum += i;
        }
        postMessage( { result: sum, count } );
    }
    ` ], { type: "plain/text" } ) );
    let firstWorker = new Worker( workerScript );
    let firstAlive = setTimeout( () => {
        firstWorker.terminate();
        console.log( "terminated" );
    }, 3000 );
    firstWorker.onmessage = function ( event ) {
        clearTimeout( firstAlive );
        console.log( event.data );
    }
    firstWorker.postMessage( 10 );

    let secondWorker = new Worker( workerScript );
    let secondAlive = setTimeout( () => {
        secondWorker.terminate();
        console.log( "terminated" );
    }, 3000 );
    secondWorker.onmessage = function ( event ) {
        clearTimeout( secondAlive );
        console.log( event.data );
    }
    secondWorker.postMessage( 5 );

}

更新 1.

{
    // Inline webworker version
    let workerScript = URL.createObjectURL( new Blob( [ `
    "use strict";
    // Recursive function
    function recursiveFunction( HitCurrent, colorCurrent, depth ) {
        // Indices
        var i, j, k;
        // Evaluation
        var arrayTemp, evaluated, e;
        // Set current color to HitCurrent
        HitCurrent.playerCurrent = colorCurrent;
        // Deep copy of arrayCurrent array
        arrayTemp = JSON.parse(JSON.stringify(HitCurrent.arrayCurrent));
        // If depth equal to 0
        if (depth == 0)
        return evaluation(HitCurrent);
        // Starting evaluation
        evaluated = -infinity;
        // Browse all possible hits
        for (i = 0; i < 8; i++) {
            for (j = 0; j < 8; j++) {
                if (HitCurrent.arrayPlayable[i][j] == 'playable') {
                    for (k = 0; k < 8; k++) {
                        // Explore line started from (i,j) with direction "k"
                        exploreHitLine(HitCurrent, i, j, k, 'drawing');
                    }
                    // Recursive call
                    e = recursiveFunction(JSON.parse(JSON.stringify(HitCurrent)), ((JSON.stringify(HitCurrent.playerCurrent) == JSON.stringify(playerBlack)) ? playerWhite : playerBlack), depth-1);
                    if ( e > evaluated ) {
                        HitCurrent.coordPlayable = [i,j];
                        evaluated = e;
                    }
                    if (e == -infinity) { HitCurrent.coordPlayable = [ i, j ]; }
                    // Restore arrayCurrent array
                    HitCurrent.arrayCurrent = JSON.parse(JSON.stringify(arrayTemp));
                }
            }
        }
        return evaluated;
    }

    onmessage = function ( event ) {
        let params = event.data;
        //postMessage( { result: recursiveFunction(  HitCurrent, HitCurrent.playerCurrent, maxNodes ) } );
        postMessage( { result: recursiveFunction( ...params ) } );
    };
    ` ], { type: "plain/text" } ) );


   // Call the recursive function and get final (a,b) results
    new Promise( resolve => {
        let HitTemp = JSON.parse(JSON.stringify(HitCurrent));
        let firstWorker = new Worker( workerScript );
        firstWorker.onmessage = function ( event ) {
            resolve( event.data ); //{ result: XXX }
        }
        firstWorker.postMessage( [ HitTemp, HitTemp.playerCurrent, maxNodes ] );
    } ).then( ( { result } ) => {
        let [ a, b ] = result.coordPlayable;
        console.log( result );
    } );
}

我的错误解释:

  1. 在"strict mode"中不能使用"eval"作为变量名。

=>

来自:eval

至:evaluated

  1. Worker.postMessage( aMessage, Transferrable ),在这种情况下,不需要使用第二个参数。

=>

来自:firstWorker.postMessage( HitTemp, HitTemp.playerCurrent, maxNodes );

至:firstWorker.postMessage( [ HitTemp, HitTemp.playerCurrent, maxNodes ] );

(https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage)

  1. 继续2,给recursiveFunction传递参数是固定的。
  • eval是关键字;使用其他一些变量名。
  • postMessage 接受一个参数(它也接受一个可转让的,但这不适用于你的情况),如果该参数不是原始值,它应该 是一个可以序列化的对象(例如,你不能传递函数 或方法直接给网络工作者)

我可能误解了您的意图,但您可能需要重新考虑递归地生成 未知 数量的网络工作者!如果您只是想释放主堆栈,只需生成 one webworker 并将函数的参数传递给它,然后在该 webworker 中同步进行递归计算,而无需生成新的 worker。产生太多的 web worker 会消耗大量资源,实际上会减慢你的计算速度!仅供参考,生成每个新的网络工作者需要大约 40 毫秒并占用资源。这是关于利用多线程计算递归函数的一般观察!这可能会有所帮助: https://softwareengineering.stackexchange.com/questions/238729/can-recursion-be-done-in-parallel-would-that-make-sense


Concerning all functions called into URL.createObjectURL( new Blob( ... )) block, Have I got to include them in this block or can I write them outside of it (as you say for evaluation and explotHitLine functions) ?

你的webworker是一个完全独立的JS文件和执行上下文;您将无法访问任何未在其上下文中定义或作为消息发送给它的内容。


顺便说一句,您的代码中存在一些错误,无论如何都无法正确编译:infinity 应该是 InfinityplayerBlackplayerWhite 未定义等