Worker i obiekty

0

Próbuję zrobić w czystym JavaScript wielowątkowosć za pomocą Workera według tych opisów:
http://blog.kamilbrenk.pl/web-workers-wielowatkowosc-w-javascript/
https://www.nafrontendzie.pl/wielowatkowy-javascript-web-worker
Doczytałem w Google, że wykorzystując URL.createObjectURL da się uniknąć podłączania innego pliku (w zależności od potrzeb funkcja workera może być albo w tym samym, albo w osobnym pliku) i to działa, sam sprawdzałem.

Mam taki kod w ramach prób:

function WorkerDef()
{
    this.ObiektWewnetrzny = [];

    this.Przeksztalc = function(Obj)
    {
        for (var I = 0; I < Obj.length; I++)
        {
            Obj[I]++;
        }
    }

    this.onmessage = function(Evt)
    {
        Obj:this.ObiektWewnetrzny = Evt.data.Obj;
        this.Przeksztalc(this.ObiektWewnetrzny);
        var OutMsg = this.ObiektWewnetrzny.join(",");
        postMessage({Msg:OutMsg, Obj:this.ObiektWewnetrzny});
    }
}

var JakisObiekt = [];

// Pewna funkcja wykonująca przekształcenie podanego obiektu
function Przeksztalc(Obj)
{
    for (var I = 0; I < Obj.length; I++)
    {
        Obj[I]++;
    }
}

var WorkerObj = new Worker(URL.createObjectURL(new Blob(["("+WorkerDef.toString()+")()"], {type: 'text/javascript'})));
WorkerObj.onmessage = function(Evt)
{
    console.log("Obiekt wewnetrzny");
    console.log(Evt.data.Obj);
    console.log("Obiekt zewnetrzny");
    console.log(JakisObiekt);
    Przeksztalc(JakisObiekt);
    Przeksztalc(JakisObiekt);
    Przeksztalc(JakisObiekt);
    console.log("Obiekt wewnetrzny");
    console.log(Evt.data.Obj);
    console.log("Obiekt zewnetrzny");
    console.log(JakisObiekt);
}

function Start()
{
    for (var I = 0; I < 10; I++)
    {
        JakisObiekt.push(I);
    }
    var InMsg = JakisObiekt;
    console.log("Obiekt przed przeksztalceniem");
    console.log(JakisObiekt);
    WorkerObj.postMessage({Msg:InMsg,Obj:JakisObiekt});
}

  1. W jaki sposób sprawić, żeby this.ObiektWewnetrzny i JakisObiekt wskazywał na jeden i ten sam obiekt (nie były to dwa różne obiekty)?
  2. W jaki sposób i gdzie napisać funkcje Przeksztalc, aby jeden i ten sam kod mógł zadziałać zarówno na this.ObiektWewnetrzny, jak i na JakisObiekt?
  3. W jaki sposób za pomocą postMessage przekazać obiekt jako taki, a nie tekst w formacie JSON, który obrazuje obiekt (przy dużych obiektach może to mieć znaczenie wydajnościowe)?
0

Nie powiem że w pełni przeczyałem i zrozumiałem, ale...

  1. Przekazywanie wiadomości pomiędzy workerem a kodem to jedynam możliwa droga komunikacji (inaczej mielibyśmy do czynienia z problemami programowania współbierznego - uwierz mi nikt tego w GUI kodzie - a takim jest JS nie chce)
  2. Przekazywana wiadomość jest kopią danych, a więc nawet jak wyślesz jakiś obiekt to Worker dostanie kopie.

Wielowątkowość powinieneś tak ogarnąć że:

  1. wysyłasz message z "robotą" do wykonania do puli workerów (tak warto mieć więcej niż jeden) (postMessage)
  2. jak worker skończy to odsyła ci wiadomość z wynikiem obliczeń lub błędem, ty to odbierasz (onmessage)

Możesz tą pule zrobić tak żeby na API były tylko Promise'y widoczne.

PS. Jak mówiłem nie przeczytałem całości więc jak nie o to Ci chodziło to sorry...

0

Właśnie sam doczytałem o jedynym sposobie przekazania wiadomości poprzez postMessage, który jako parametr przyjmuje obiekt JSON i to poprzez wartość, więc w jedną i drugą stronę powstaje kopia obiektu.

Znalazłem coś takiego: https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage
Wydaje się, że drugi parametr postMessage to przekazanie referencji na obiekt. Okazuje się, że ten obiekt musi dziedziczyć po Transferable, a nimi są ArrayBuffer, MessagePort, ImageBitmap i OffscreenCanvas (nie wiadomo, czy tylko te, czy między innymi te).

Próbowałem zmian w kodzie, ale to nic nie dało:

function WorkerDef()
{
    this.ObiektWewnetrzny = {X:[]};

    this.Przeksztalc = function(Obj)
    {
        for (var I = 0; I < Obj.X.length; I++)
        {
            Obj.X[I]++;
        }
    }

    this.onmessage = function(Evt)
    {
        this.ObiektWewnetrzny = Evt.data.Obj;
        this.Przeksztalc(this.ObiektWewnetrzny);
        this.Przeksztalc(Evt.data.Obj);
        var OutMsg = "OK";
        postMessage({Msg:OutMsg, Obj:this.ObiektWewnetrzny}, ObiektWewnetrzny);
    }
}

var JakisObiekt = {X:[]};

// Pewna funkcja wykonująca przekształcenie podanego obiektu
function Przeksztalc(Obj)
{
    for (var I = 0; I < Obj.X.length; I++)
    {
        Obj.X[I]++;
    }
}

var WorkerObj = new Worker(URL.createObjectURL(new Blob(["("+WorkerDef.toString()+")()"], {type: 'text/javascript'})));
WorkerObj.onmessage = function(Evt)
{
    console.log("Obiekt wewnetrzny");
    console.log(Evt.data.Obj.X);
    console.log("Obiekt zewnetrzny");
    console.log(JakisObiekt.X);
}

function Start()
{
    for (var I = 0; I < 10; I++)
    {
        JakisObiekt.X.push(I);
    }
    console.log("Obiekt przed przeksztalceniem");
    console.log(JakisObiekt.X);
    Przeksztalc(JakisObiekt);
    console.log(JakisObiekt.X);

    
    WorkerObj.postMessage({Msg:"Test",Obj:JakisObiekt}, JakisObiekt);
}

Start();

Można całą logikę zawrzeć w Worker i poprzez wiadomości tylko uruchamiać funkcje i odbierać wynik. Jednak z drugiej strony, jak przekazywać obiekt jako taki pomiędzy instancjami Worker, jeżeli chce się zrealizować pulę obiektów Worker?

Jak jako drugi parametr dałem w [], to jest błąd, że obiekt nie może być sklonowany. Mi nie chodzi o sklonowanie, tylko o przekazanie referencji do obiektu.

0

Właśnie przerabiam swój projekt tak, żeby korzystał z Workera i to w nim było wszystko, a z UI komunikował się za pomocą komunikatów zawierających JSON.

Zrobiłem w Firefox taki test, że jak się wyśle do Workera kilka komunikatów jeden za drugim, to Worker wykonuje funkcje obsługi komunikatów sekwencyjnie według kolejności wysłanych komunikatów. W tym przypadku, w którym to potrzebuję, taka właściwość jest mi na rękę. Czy to jest cecha założona w specyfikacji Workera, czy akurat Firefox tak działa, a w innych przeglądarkach może być inaczej? Innymi słowy, jeden Worker może generować tylko jeden dodatkowy wątek.

1 użytkowników online, w tym zalogowanych: 0, gości: 1