1 Востаннє редагувалося Betterthanyou (22.09.2016 02:03:33)

Тема: мережі Петрі, багатопотокове обчислення прикладу

Завдання. За допомогою мережі Петрі виконати (багатопотокове) обчислення прикладу Zn = SIN( Un + COS( Vn ) ) * COS( SIN( Un + Vn ) )

Мережа Петрі яку я склав

https://replace.org.ua/extensions/om_images/img/57e31f0320052/53aa06c8488389e259e576e2b2647575.png
Кому лінь читати статтю про мережі Петрі, дивіться короткі пояснення
коло - це Вузол
рисочка або чотирикутник - Стан
Якщо вузол маркований (має 1 або більше крапку в колі) то можна здійснювати перехід на ін. вузол.
Стан перевіряє чи всі вузли марковані, якщо це так то здійсниться перехід.
Але є багато чого іншого, наприклад заперечення, вага дуг та інше.

Це код
#include <iostream>
#include <Windows.h>
#include <process.h>
#include <cmath>
/*
Zn = SIN( Un + COS( Vn ) ) * COS( SIN( Un + Vn ) )
*/
namespace example
{
    struct sMathOperation
    {
        sMathOperation() = default;
        sMathOperation(void *par);
        double a;
        double b;
        double result;
    };

    sMathOperation::sMathOperation(void *par)
    {
        sMathOperation *obj = reinterpret_cast<sMathOperation*>(par);
        this->a = obj->a;
        this->b = obj->b;
        this->result = obj->result;
    }    

    struct sMathFunction
    {
        sMathFunction() = default;
        sMathFunction(void *par);
        double (*mathFunction)(double);
        double a;
        double result;
    };

    sMathFunction::sMathFunction(void *par)
    {
        sMathFunction *obj = reinterpret_cast<sMathFunction*>(par);
        this->mathFunction = obj->mathFunction;
        this->a = obj->a;
        this->result = obj->result;
    }

    class example
    {
    public:
        example(double Un_, double Vn_);
        double getResult();
    private:
        static void add(void *par);
        static void multiply(void *par);
        static void mathFunction(void *par);
        double Un;
        double Vn;
        double result;
    };

    example::example(double Un_, double Vn_) : Un(Un_), Vn(Vn_)
    {        
        //array
        HANDLE hand[2];

        //calculation example Un + Vn
        sMathOperation *sAddOperation = new sMathOperation;
        sAddOperation->a = Un;
        sAddOperation->b = Vn;
        hand[0] = (HANDLE)_beginthread(&example::add, 0, sAddOperation);

        //calculation example COS( Vn )
        sMathFunction *sCosFunction = new sMathFunction;
        sCosFunction->a = Vn;
        sCosFunction->mathFunction = cos;
        hand[1] = (HANDLE)_beginthread(&example::mathFunction, 0, sCosFunction);

        //wait previous operations
        WaitForMultipleObjects(2, hand, true, INFINITE);

        //calculation example SIN( Un + Vn )
        sMathFunction *sSinFunction = new sMathFunction;
        sSinFunction->a = sAddOperation->result;
        sSinFunction->mathFunction = sin;
        hand[0] = (HANDLE)_beginthread(&example::mathFunction, 0, sSinFunction);
        //delete previous object
        delete sAddOperation;        

        //calculation example Un + COS( Vn )
        sAddOperation = new sMathOperation;
        sAddOperation->a = Un;
        sAddOperation->b = sCosFunction->result;
        hand[1] = (HANDLE)_beginthread(&example::add, 0, sAddOperation);
        //delete previous object
        delete sCosFunction;

        //wait previous operations
        WaitForMultipleObjects(2, hand, true, INFINITE);

        //calculation example COS( SIN( Un + Vn ) )
        sCosFunction = new sMathFunction;
        sCosFunction->a = sSinFunction->result;
        sCosFunction->mathFunction = cos;
        hand[0] = (HANDLE)_beginthread(&example::mathFunction, 0, sCosFunction);
        //delete previous object
        delete sSinFunction;

        //calculation example SIN( Un + COS( Vn ) )
        sSinFunction = new sMathFunction;
        sSinFunction->a = sAddOperation->result;
        sSinFunction->mathFunction = sin;
        hand[1] = (HANDLE)_beginthread(&example::mathFunction, 0, sSinFunction);
        //delete previous object
        delete sAddOperation;

        //wait previous operations
        WaitForMultipleObjects(2, hand, true, INFINITE);

        //calculation example SIN( Un + COS( Vn ) ) * COS( SIN( Un + Vn ) )
        sMathOperation *sMultiplyOperation = new sMathOperation;
        sMultiplyOperation->a = sCosFunction->result;
        sMultiplyOperation->b = sSinFunction->result;
        hand[0] = (HANDLE)_beginthread(&example::multiply, 0, sMultiplyOperation);
        //delete previous objects
        delete sCosFunction;
        delete sSinFunction;

        //wait previous operation
        WaitForSingleObject(hand[0], INFINITE);
        
        //result        
        result = sMultiplyOperation->result;

        //delete previous object
        delete sMultiplyOperation;
    }

    double example::getResult()
    {
        return result;
    }

    void example::add(void *par)
    {
        sMathOperation *obj = (sMathOperation *)par;
        obj->result = obj->a + obj->b;
    }

    void example::multiply(void *par)
    {
        sMathOperation *obj = (sMathOperation *)par;
        obj->result = obj->a * obj->b;
    }
    
    void example::mathFunction(void *par)
    {
        sMathFunction *obj = (sMathFunction *)par;
        obj->result = obj->mathFunction(obj->a);
    }

}

int main(int argc, char *argv[]) try
{    
    double Un,
        Vn;

    //get Un, Vn
    std::cout << "Enter Un ->";
    std::cin >> Un;
    std::cout << "Enter Vn ->";
    std::cin >> Vn;

    //multi-threaded
    example::example obj(Un, Vn);

    //single-threaded
    double Zn;
    Zn = sin(Un + cos(Vn)) * cos(sin(Un + Vn));

    //output results
    std::cout << "(multi-threaded) Zn = " << obj.getResult() << std::endl;    
    std::cout << "(single-threaded) Zn = " << Zn << std::endl;
    
    system("pause");
    return 0;
}
catch (...)
{
    std::cout << "\ncritical error!\n";
    system("pause");
    return -1;
}

В ролі "стану" виступають функції WaitForMultiplyObjects i WaitForSingleObject, вони чекають виконання попередніх дій тим самим не даючи запустити програмі нові потоки.

Я дивився як можна у функцію, що буде виконуватися новим потоком, передати декілька аргументів і знайшов пораду використовувати структури (struct), а в функцію передавати об'єкт структури

Ось і все, не знаю чи правильно я зробив (можливо це робиться якось простіше), але програма робити і показує правильний результат

2 Востаннє редагувалося ReAl (23.09.2016 00:27:20)

Re: мережі Петрі, багатопотокове обчислення прикладу

Оскільки я про мережі Петрі лише «щось читав *дцять років тому» + зараз зазирнув у Вікіпедію, можу помилятися, але:

(тут було трохи термінологічної плутанини, витерто)

На вході мережі не вистачає станів з породженням Vn, Un і переходів, які їх розмножують у дві гілки. Зараз у лівій послідовності є Vn і у правій є Vn — хто такі, де взялися, чи однаковий у них «n» ;) ?
У гілки мають іти копії одного й того ж значення, тому фішки мають розмножитися і піти у різні вузли.

Виходить якась така мережа

https://replace.org.ua/extensions/om_images/img/57e457870a324/example2.png
Вузли еліпсами, щоб менше місця.
Переходи зробив прямокутниками з label, щоб не підписувати іншими способами.
Не бачу особливого сенсу в розділенні на стани нерозгалужених ланцюжків у нижній частині перед злиттям в обчисленні добутку. Сенс у цьому міг би бути при апаратній реалізації (FPGA) для розділення на дрібні шматки з регістрами між ними з метою підвищення частоти роботи за допомогою конвеєризації, у програмно-багатопоточній моделі це лише зайві потоки-переходи, які швидкість не піднімуть (хіба якщо обчислення доволі складні, а потоків все одно менше, ніж ядер).

Всі потоки переходів мають жити постійно, до кінця програми, бо мережа має існувати постійно вся, на вхід прилітають дані-фішки, десь всередині мережі теоретично можуть знаходитися недооброблені. Тобто оці Un, Vn — саме n-ті. Коли вони посередині, попередні n-1-ші вийшли на вихід мережі, n+1-ші вже зайшли. Взагалі всі вузли можуть бути заповнені даними різного віку. Ті вузли-Object-и , яких WaitFor — мають бути якісь mailbox, причому перехід-потік має упиратися, якщо у вихідному вузлі нема місця, бо аж наступний перехід ще не забрав, оскільки чекає також іншу гілку.

У Вашому ж прикладі коду  обчислення фактично послідовні, а не паралельні. Там неможливо, щоб, наприклад, cos(sin(Un+Vn)) обчислювався «одночасно» (а то і пізніше), наприклад, Un+1+cos(Vn+1).

Тобто головна програма має створити всі потоки, поєднані mailbox-ами, а потім вкидати на вхід пари Un, Vn і забирати з виходу результати.

Подякували: Betterthanyou, 0xDADA11C7, leofun013

3

Re: мережі Петрі, багатопотокове обчислення прикладу

p.s. Картинку мережі вище написано мовою dot, після чого перегнано у png командою

dot -T png example2.dot >example2.png
Код картинки мережі
digraph calculation {
// Здається, позиція не може розмножити елементи, тобто щоб кинути у дві дуги,
// треба мати дві фішки. Тому треба генерувати дві копії у поміжні стани?
    subgraph places {
        //node [shape=circle,fixedsize=true,width=1.8];
        node [shape=oval,fixedsize=true,width=2.2];
        Vn1 [label="Vn"];
        Vn2 [label="Vn"];
        Un1 [label="Un"];
        Un2 [label="Un"];
        "cos(Vn)";
        "Un+cos(Vn)";
        "sin(Un+cos(Vn))";
        "Un+Vn";
        "sin(Un+Vn)";
        "cos(sin(Un+Vn))";
        result [label = "sin(Un+cos(Vn))\n* cos(sin(Un+Vn))"];
    }
    subgraph transitions {
        node [shape=rect,height=0.2,width=2];
        "Produce Un";
        "Produce Vn";
        "Calculate cos(Vn)";
        "Calculate Un+cos(Vn)";
        "Calculate sin(Un+cos(Vn))";
        "Calculate Un+Vn";
        "Calculate sin(Un+Vn)";
        "Calculate cos(sin(Un+Vn))";
        calc_result [label="Calculate sin(Un+cos(Vn)) * cos(sin(Un+Vn))"];
        "Yield result";
    }

    //rank = same {Vn1, Vn2, Un1, Un2};
    rank = same {Vn1, Vn2, Un1};
    rank = same {Un2, "cos(Vn)"};

    "Produce Un" -> Un1;
    "Produce Vn" -> Vn1;
    "Produce Un" -> Un2;
    "Produce Vn" -> Vn2;

    Vn1 -> "Calculate cos(Vn)" -> "cos(Vn)";
    Vn2 -> "Calculate Un+Vn";

    Un1 -> "Calculate Un+Vn" -> "Un+Vn";
    Un2 -> "Calculate Un+cos(Vn)";

    "cos(Vn)" -> "Calculate Un+cos(Vn)" -> "Un+cos(Vn)";
    "Un+cos(Vn)" -> "Calculate sin(Un+cos(Vn))" -> "sin(Un+cos(Vn))";

    "Un+Vn" -> "Calculate sin(Un+Vn)" -> "sin(Un+Vn)"
        -> "Calculate cos(sin(Un+Vn))" -> "cos(sin(Un+Vn))";

    "sin(Un+cos(Vn))" -> calc_result;
    "cos(sin(Un+Vn))" -> calc_result -> result;
    result -> "Yield result";
}

Вузли і дуги переважно названі своїми ж label, щоб не писати окремі атрибути, але для найдовших міток таки дав короткі імена з атрибутами, щоб не так у частині переходів мерехтіло в очах.

Подякували: Betterthanyou, 0xDADA11C7, leofun013

4 Востаннє редагувалося Betterthanyou (23.09.2016 00:54:02)

Re: мережі Петрі, багатопотокове обчислення прикладу

Да, щось я все це зробив неправильно.

Я думав що Vn, Un це змінні, але це масив, я уточнив умову.
Та мережа що я склав точно не правильна, мережа яку ви зробили є правильною

Дякую за допомогу