AS3项目,AIR开发,使用多线程Worker,怎么让多个相同的子线程判断自己是线程池中的哪一个??
如题,假如我想使用4个相同的子线程,命名为worker1-worker4,这4个线程同时执行不同的任务。线程的创建和启动都没有问题,问题在于:如何让这4个子线程分别知道自己是哪一个线程?或者说自己在线程池中的名称/索引?不搞清这个问题,子线程就没法和主线程正确建立MessageChannel,也就没法通信。
我翻遍了所有官方的api,没找到任何有帮助的信息,也没找到相关的机制(也可能是我太菜),网上能搜到的所有方法也试过了,都失败了。
我的想法是在每个子线程内使用WorkerDomain.current.listWorkers()方法获取线程池,然后逐一判断池内某项是否等于Worker.current。看起来逻辑似乎合理,以下是我按照这个思路写的小样,运行起来虽然不报错,但也不正确。
有没有人能指出更好的思路?或者完善我的思路,Worker这玩意太难用了!
1 2 import flash.events.Event; 3 import flash.system.MessageChannel; 4 import flash.system.Worker; 5 import flash.system.WorkerDomain; 6 7 var curID: int = -1 8 9 var mainToWorker1: MessageChannel 10 var mainToWorker2: MessageChannel 11 var mainToWorker3: MessageChannel 12 var mainToWorker4: MessageChannel 13 14 var worker1ToMain: MessageChannel 15 var worker2ToMain: MessageChannel 16 var worker3ToMain: MessageChannel 17 var worker4ToMain: MessageChannel 18 19 if (Worker.current.isPrimordial) { 20 var worker1 = WorkerDomain.current.createWorker(this.loaderInfo.bytes, true); 21 var worker2 = WorkerDomain.current.createWorker(this.loaderInfo.bytes, true); 22 var worker3 = WorkerDomain.current.createWorker(this.loaderInfo.bytes, true); 23 var worker4 = WorkerDomain.current.createWorker(this.loaderInfo.bytes, true); 24 25 mainToWorker1 = Worker.current.createMessageChannel(worker1); 26 mainToWorker2 = Worker.current.createMessageChannel(worker2); 27 mainToWorker3 = Worker.current.createMessageChannel(worker3); 28 mainToWorker4 = Worker.current.createMessageChannel(worker4); 29 30 worker1ToMain = worker1.createMessageChannel(Worker.current); 31 worker2ToMain = worker2.createMessageChannel(Worker.current); 32 worker3ToMain = worker3.createMessageChannel(Worker.current); 33 worker4ToMain = worker4.createMessageChannel(Worker.current); 34 35 worker1.setSharedProperty("mainToWorker1", mainToWorker1); 36 worker2.setSharedProperty("mainToWorker2", mainToWorker2); 37 worker3.setSharedProperty("mainToWorker3", mainToWorker3); 38 worker4.setSharedProperty("mainToWorker4", mainToWorker4); 39 40 worker1.setSharedProperty("worker1ToMain", worker1ToMain); 41 worker2.setSharedProperty("worker2ToMain", worker2ToMain); 42 worker3.setSharedProperty("worker3ToMain", worker3ToMain); 43 worker4.setSharedProperty("worker4ToMain", worker4ToMain); 44 45 worker1ToMain.addEventListener(Event.CHANNEL_MESSAGE, fstep3); 46 worker2ToMain.addEventListener(Event.CHANNEL_MESSAGE, fstep3); 47 worker3ToMain.addEventListener(Event.CHANNEL_MESSAGE, fstep3); 48 worker4ToMain.addEventListener(Event.CHANNEL_MESSAGE, fstep3); 49 50 worker1.start(); 51 worker2.start(); 52 worker3.start(); 53 worker4.start(); 54 55 } else { 56 curID = getCurID() 57 this["mainToWorker" + curID] = Worker.current.getSharedProperty("mainToWorker" + curID); 58 this["worker" + curID + "ToMain"] = Worker.current.getSharedProperty("worker" + curID + "ToMain"); 59 this["mainToWorker" + curID].addEventListener(Event.CHANNEL_MESSAGE, fstep2); 60 61 } 62 63 64 function getCurID() { 65 //这个获取ID的方式有没有问题?每个子线程中执行listWorkers()返回的结果是否一致?不知道 66 var v = WorkerDomain.current.listWorkers() 67 for (var a: int = 0; a < v.length; a++) { 68 if (v[a].isPrimordial) { 69 v.splice(a, 1) 70 break 71 } 72 } 73 for (var b: int = 0; b < v.length; b++) { 74 if (v[b] == Worker.current) { 75 return b + 1 76 } 77 } 78 } 79 80 81 //步骤1:点击屏幕发送初始消息 82 stage.addEventListener("click", f1) 83 function f1(e) { 84 txt1.appendText("\n初始消息已发送\n") 85 mainToWorker1.send("") 86 mainToWorker2.send("") 87 mainToWorker3.send("") 88 mainToWorker4.send("") 89 } 90 91 //步骤2:子线程收到消息,发送自己的编号 92 function fstep2(event: Event) { 93 this["worker" + curID + "ToMain"].send(curID) 94 } 95 96 //步骤3:主线程接收子线程编号 97 function fstep3(event: Event): void { 98 txt1.appendText("从分支线程发来的消息是\"" + event.target.receive() + "\"\n") 99 } 100
要让每个子线程知道自己在线程池中的位置,你可以在主线程中设置一个全局变量(例如workerIDs),并将它分配给每个子线程。你可以使用WorkerDomain.current.listWorkers()方法获得所有线程的数组,然后在循环中将每个线程的workerID属性设置为相应的索引值。
你的想法基本上是正确的,即通过 WorkerDomain.current.listWorkers() 方法获取线程池对象,然后比对当前子线程是否在线程池中,以此确定当前子线程的 ID。但是在实现时,有些问题需要注意:
WorkerDomain.current.listWorkers() 返回的数组中包含主线程和所有子线程,因此你需要先将主线程排除,否则 getCurID() 方法返回的 ID 将不是 1-4 而是 0-3。
判断子线程是否在线程池中时,不能使用 == 运算符进行比较,因为这会导致比较的始终是子线程对象的引用地址,而不是对象本身。正确的做法是使用 ObjectUtil.compare() 方法比较两个对象是否相等。
在主线程中给子线程设置属性时,属性名应该与子线程中获取属性时使用的名字相同。你现在的实现是将属性名加上了序号,而在子线程中获取属性时却没有加序号。
import flash.events.Event;
import flash.system.MessageChannel;
import flash.system.Worker;
import flash.system.WorkerDomain;
import mx.utils.ObjectUtil;
var curID:int = -1;
var mainToWorker:Vector.<MessageChannel> = new Vector.<MessageChannel>();
var workerToMain:Vector.<MessageChannel> = new Vector.<MessageChannel>();
if (Worker.current.isPrimordial) {
for (var i:int = 0; i < 4; i++) {
var worker:Worker = WorkerDomain.current.createWorker(this.loaderInfo.bytes, true);
mainToWorker[i] = Worker.current.createMessageChannel(worker);
workerToMain[i] = worker.createMessageChannel(Worker.current);
worker.setSharedProperty("mainToWorker", mainToWorker[i]);
worker.setSharedProperty("workerToMain", workerToMain[i]);
workerToMain[i].addEventListener(Event.CHANNEL_MESSAGE, onWorkerMessage);
worker.start();
}
} else {
curID = getCurID();
mainToWorker = Worker.current.getSharedProperty("mainToWorker");
workerToMain = Worker.current.getSharedProperty("workerToMain");
mainToWorker[curID - 1].addEventListener(Event.CHANNEL_MESSAGE, onMainMessage);
}
function getCurID():int {
var workers:Array = WorkerDomain.current.listWorkers();
for (var i:int = 0; i < workers.length; i++) {
if (!workers[i].isPrimordial && ObjectUtil.compare(workers[i], Worker.current) == 0) {
return i + 1;
}
}
return -1;
}
function onWorkerMessage(event:Event):void {
var workerToMainIndex:int = workerToMain.indexOf(event.currentTarget);
trace("Worker " + workerToMainIndex + " received message from main thread");
}
function onMainMessage(event:Event):void {
var mainToWorkerIndex:int = mainToWorker.indexOf(event.currentTarget);
trace("Main thread received message from worker " + mainToWorkerIndex);
}
这个代码中,我将 mainToWorker 和 workerToMain 从一般的变量改成了 Vector.<MessageChannel>,这样可以更方便