Трябва ли да синхронизирам RPC в този сценарий?

Вчера разбрах, че това, което правя, потенциално може да бъде проблем, ако не синхронизирам асинхронните си заявки - въпросът е дали наистина трябва.

Нека вземем следния сценарий: Имам изглед от най-високо ниво, който предава списък с данни List<SomeObject> надолу към неговите подизгледи. Всички презентатори на подниво ще работят с този списък с данни чрез добавяне, премахване и модифициране на елементи от този списък.

Да приемем, че потребител прави нещо в потребителския интерфейс и след това натиска бутона „Запазване“ на множество екземпляри на изглед на подниво, което ще извика SubLevelPresenter.onSaveSomeObjectButtonClick(). Това ще извърши RPC повикване, чийто резултат ще бъде добавен към списъка List<Somebject>.

Моето предположение е, че е възможно, ако потребителят щракне върху два различни бутона за запазване, данните да пристигнат по едно и също време, задействайки onSaveSomeObjectButtonClick() едновременно, което ще доведе до едновременен достъп до List<SomeObject>.

Трябва ли да синхронизирам тези действия чрез прилагане на опашка, напр. или съм в безопасност поради някаква вътрешна магия на GWT/JavaScript, която не виждам тук?

public class TopLevelPresenter {

    private List<SomeObject> someObjectList = new ArrayList<>();    
    private List<SubLevelPresenter> cache = new ArrayList<>();

    public void TopLevelPresenter(TopLevelModel topLevelModel, TopLevelView topLevelView) {

        this.topLevelModel = topLevelModel;
        this.topLevelView = topLevelView;

        for(int i = 0; i < topLevelModel.getNumOfSubViews(); i++) {

            // Pass the data
            SubLevelModel subLevelModel = new SubLevelModel(someObjectList);
            SubLevelView subLevelView = new SubLevelView();         
            SubLevelPresenter subLevelPresenter = new SubLevelPresenter(subLevelModel, subLevelView);
            cache.add(subLevelPresenter);
        }
    }
}

public class SubLevelModel() {

    private List<SomeObject> someObjectList;

    public SubLevelModel(List<SomeObject> someObjectList) {
        this.someObjectList = someObjectList;
    }

    public void addSomeObject(SomeObject someObject) {
        this.someObjectList.add(someObject);
    }

    public void removeSomeObject(SomeObject someObject) {
        this.someObjectList.remove(someObject);
    }
}

public class SubLevelPresenter() {

    private SomeServiceAsync someService = /* .. */;
    private SubLevelView subLevelView;  
    private SubLevelModel subLevelModel;

    public SubLevelPresenter(SubLevelModel subLevelModel, SubLevelView subLevelView) {
        this.subLevelView = subLevelView;
        this.subLevelModel = subLevelModel;
    }

    public void onSaveSomeObjectButtonClick() {

        SomeObject toSave = this.subLevelView.getSuggestionBox().getSelection();

        someService.saveSomeObject(toSave, new AsyncCallback<SomeObject>() {

            @Override
            public void onFailure(Throwable caught) {
                Window.alert("RPC to saveSomeObject() failed.");
            }

            @Override
            public void onSuccess(SomeObject savedObject) {
                SubLevelPresenter.this.subLevelModel.addSomeObject(savedObject);
            }
        });
    }
}

person Stefan Falk    schedule 25.08.2015    source източник


Отговори (2)


Ключовата дума synchronized е безполезна (и игнорирана от GWT) в кода от страна на клиента, като се има предвид, че JavaScript е еднонишков. Дори RPC отговорите да са пристигнали по едно и също време, те ще бъдат поставени на опашка в цикъла на събития на JavaScript и ще бъдат обработени един след друг.

Обърнете внимание, че има грешки в браузърите (а именно Firefox), водещи до условия на състезание, но те не могат да бъдат открити и/или заобиколени толкова лесно, колкото изпускането на някаква synchronized (или еквивалентна) ключова дума в кода. Също така имайте предвид, че грешка във Firefox е свързана с alert() и confirm().

person Thomas Broyer    schedule 25.08.2015
comment
Добре, това си мислех, но не бях сигурен. И така, да кажем, че наистина се страхувам от състояние на състезание, има ли смисъл да изградя опашка, която изпраща една заявка след друга? Както изглежда, това би било просто решение за Firefox. - person Stefan Falk; 25.08.2015
comment
Това просто ще премести проблема в управлението на опашката. И хвърлете баланса: вероятността и честотата на 2 заявки по време на полет, вероятността за състояние на състезание в браузър с грешки (имайте предвид, че не знаем дали има такъв браузър, Проблемът с Firefox изглежда свързан с блокиращите alert() и confirm(), така че зависи дали използвате някое от тях), въздействието върху вашия код (вкл. поддръжка), въздействието върху производителността (и възприеманата производителност за потребителя), последствията, ако това се случи (поправка за потребителя, ако забележи, че е само на едно опресняване) - person Thomas Broyer; 25.08.2015
comment
Добре, разбрах :) Благодаря! - person Stefan Falk; 25.08.2015

За вашия сценарий изглежда е добре няколко екземпляра да поставят данни във вашия списък след RPC. Все пак може да е добро решение да използвате синхронизиран списък тук, за да поставите на опашка едновременен достъп.

Collections.synchronizedList(new ArrayList<String>());
person ArcTanH    schedule 25.08.2015
comment
…или CopyOnWriteArrayList. Но това се отнася само за код, който се изпълнява в JVM, а не когато е компилиран в JavaScript с помощта на GWT, защото JavaScript. - person Thomas Broyer; 25.08.2015