Kirjoittaja Aihe: [ Ratkaistu ] c++ ja tehtävien ajastaminen  (Luettu 4858 kertaa)

teele

  • Käyttäjä
  • Viestejä: 818
    • Profiili
[ Ratkaistu ] c++ ja tehtävien ajastaminen
« : 22.02.16 - klo:09.26 »
Jos on tilanne sellainen, että halutaan esimerkiksi ajastaa kaksi tehtävää niin, että toinen tehdään 2 sekunnin välein ja toinen 3 sekunnin välein, niin millainen olisi hyvä ratkaisu c++ :ssa.

Tällainen ratkaisu ei vaikuttaisi kovin edistyneeltä

Koodia: [Valitse]
last-time1 = last-time2 = current_time()
while(1)
{
    if( curent_time – last-time1 > 2)
      {
          do_task1();    // tämän olisi hyvä kestää alle sekunnin
           last_time1 = current_time()
      }                       
 
    if( curent_time – last-time2 > 3)
      {
          do_task2();     // tämän pitäisi kestää alle 2 sekuntia
           last_time2 = current_time()
      }

     do_other_tasks()  // näistä siis pitäisi tietää, että niiden
                                  // tekeminen vie aina alle 2 sekuntia
}

Voisiko c++ :lla tehdä jotenkin näin

Koodia: [Valitse]
main()
{
   do_task1()  // miten tämä saataisiin ajastumaan 2 sekunnin välein
   do_task2()  // miten tämä saataisiin ajastumaan 3 sekunnin välein
   do_other_tasks() // tämä olisi sitten normaalia koodia

}
« Viimeksi muokattu: 28.02.16 - klo:08.49 kirjoittanut teele »

kamara

  • Käyttäjä
  • Viestejä: 2952
    • Profiili
Vs: c++ ja tehtävien ajastaminen
« Vastaus #1 : 22.02.16 - klo:14.36 »
Kannattaa laittaa säikeeseen ja nukkumaan halutuksi ajaksi.

En tiedä tarvitaanko tavallista säiettä kaveriksi, kun en ole C++:lla pahemmin ohjelmoinnut, mutta  näistä voi olla vähän apua...

http://en.cppreference.com/w/cpp/thread/sleep_for

http://www.cplusplus.com/reference/thread/thread/

nm

  • Käyttäjä
  • Viestejä: 16252
    • Profiili
Vs: c++ ja tehtävien ajastaminen
« Vastaus #2 : 22.02.16 - klo:14.52 »
Boostilla asynkroniset ajastukset hoituvat aika helposti joko yhdessä tai useassa säikeessä: http://www.boost.org/doc/libs/1_40_0/doc/html/boost_asio/tutorial.html (esimerkit Timer.2 - Timer.5)

C++11:ssä on std::async, joka ilmeisesti myös helpottaa rinnakkaisten ajastettujen tehtävien koodausta.

Huomaa, että ohjelman kompleksisuus pyrkii lähtemään käsistä heti, kun säikeet ja rinnakkaisuus tulee mukaan kuvioihin. Kannattaa etsiä vaikka joku suhteellisen tuore kirja tai yliopiston kurssimateriaalit aiheesta.

teele

  • Käyttäjä
  • Viestejä: 818
    • Profiili
Vs: c++ ja tehtävien ajastaminen
« Vastaus #3 : 27.02.16 - klo:16.15 »
Tutustuin sitten säikeisiin ja kokeilin tällaista ohjelmaa

Koodia: [Valitse]
#include <iostream>
#include <thread>
#include <future>

#include <chrono>
#include <ctime>
 

long fibonacci(unsigned n)
{
    if (n < 2) return n;
    return fibonacci(n-1) + fibonacci(n-2);
}


void initiazer(std::promise<int> *promObj, long val, int n)
{
    std::cout<<"ollaan säikeessä " << n <<std::endl;
    promObj->set_value(fibonacci(val));
}


int main()
{
    std::chrono::time_point<std::chrono::system_clock> start, end;
    start = std::chrono::system_clock::now();
   
    std::promise<int> promiseObj1;
    std::future<int> futureObj1 = promiseObj1.get_future();
    std::thread th1(initiazer, &promiseObj1, 42, 1);

    std::promise<int> promiseObj2;
    std::future<int> futureObj2 = promiseObj2.get_future();
    std::thread th2(initiazer, &promiseObj2, 42, 2);

    std::promise<int> promiseObj3;
    std::future<int> futureObj3 = promiseObj3.get_future();
    std::thread th3(initiazer, &promiseObj3, 42, 3);

    std::cout<<futureObj1.get()<<std::endl;
    std::cout<<futureObj2.get()<<std::endl;
    std::cout<<futureObj3.get()<<std::endl;


    th1.join();
    th2.join();
    th3.join();

    end = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_seconds = end-start;
    std::time_t end_time = std::chrono::system_clock::to_time_t(end);
 
    std::cout << "finished computation at " << std::ctime(&end_time)
              << "elapsed time: " << elapsed_seconds.count() << "s\n";

    return 0;
}

Ohjelmassa käynnistetään kolme säiettä, joissa jokaisessa on edellisen kerran fibonacci-laskenta.

Pari kysymystä tuli esille.

Miksi promise<long> ei käänny, vaikka yritän plauttaa fibonacci() :n long arvon.

Toinen kysymys on, miksi näiden kolmen säikeen suorittamien kestää noin 3 kertaa kauemmin kuin tavallisen fibonacci-ohjelman suorittaminen. Jos tavalliseen fibonacciin menee noin 2 sekuntia ilman optimointeja, kestää näiden 3 säikeen suorittaminen noin 6 sekuntia. Muistaakseni kahden säikeen suorittamien kesti noin 4 sekuntia.

Pitäisikö käyttää joitain erikoisvalitsimia käännettäessä. Nyt näyttää siltä, että en ole onnistunut säikeistämään ohjelmaa yhtään.

« Viimeksi muokattu: 28.02.16 - klo:06.44 kirjoittanut teele »

nm

  • Käyttäjä
  • Viestejä: 16252
    • Profiili
Vs: c++ ja tehtävien ajastaminen
« Vastaus #4 : 27.02.16 - klo:16.51 »
Miksi promise<long> ei käänny, vaikka yritän plauttaa fibonacci() :n long arvon.

Muutitko sen longiksi kaikkiin kohtiin ja myös futureen?

Toinen kysymys on, miksi näiden kolmen säikeen suorittamien kestää noin 3 kertaa kauemmin kuin tavallisen fibonacci-ohjelman suorittaminen.

futureObj1.get() jää odottamaan, että promise saa arvon, mikä tapahtuu vasta kun säikeen th1 laskenta päättyy. Sama juttu muissa säikeissä. Ohjelmasi suorittaa siis jokaisen säikeen peräkkäin eikä rinnakkain. Käynnistä ensin kaikki säikeet ja odottele vasta sitten tuloksia, niin alkaa tapahtua.

Koodia: [Valitse]
#include <iostream>
#include <thread>
#include <future>

#include <chrono>
#include <ctime>
 

unsigned long long fibonacci(unsigned long long n)
{
    if (n < 2) return n;
    return fibonacci(n-1) + fibonacci(n-2);
}


void initiazer(std::promise<unsigned long long> *promObj, unsigned long long val, int n)
{
    std::cout<<"ollaan säikeessä " << n <<std::endl;
    promObj->set_value(fibonacci(val));
}


int main()
{
    std::chrono::time_point<std::chrono::system_clock> start, end;
    start = std::chrono::system_clock::now();
   
    std::promise<unsigned long long> promiseObj1;
    std::future<unsigned long long> futureObj1 = promiseObj1.get_future();
    std::thread th1(initiazer, &promiseObj1, 42, 1);

    std::promise<unsigned long long> promiseObj2;
    std::future<unsigned long long> futureObj2 = promiseObj2.get_future();
    std::thread th2(initiazer, &promiseObj2, 42, 2);

    std::promise<unsigned long long> promiseObj3;
    std::future<unsigned long long> futureObj3 = promiseObj3.get_future();
    std::thread th3(initiazer, &promiseObj3, 42, 3);

    th1.join();
    th2.join();
    th3.join();

    std::cout<<futureObj1.get()<<std::endl;
    std::cout<<futureObj2.get()<<std::endl;
    std::cout<<futureObj3.get()<<std::endl;

    end = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_seconds = end-start;
    std::time_t end_time = std::chrono::system_clock::to_time_t(end);
 
    std::cout << "finished computation at " << std::ctime(&end_time)
              << "elapsed time: " << elapsed_seconds.count() << "s\n";

    return 0;
}
« Viimeksi muokattu: 27.02.16 - klo:16.53 kirjoittanut nm »

teele

  • Käyttäjä
  • Viestejä: 818
    • Profiili
Vs: c++ ja tehtävien ajastaminen
« Vastaus #5 : 28.02.16 - klo:08.48 »
Kiitos, nyt alkoi tapahtua  :)

Koodia: [Valitse]
#include <iostream>
#include <thread>
#include <future>
#include <chrono>
#include <ctime>
#include <mutex>
 
long fibonacci( int n)
{
    if (n < 2) return n;
    return fibonacci(n-1) + fibonacci(n-2);
}

std::mutex m; // initiazeria varten

void initiazer(std::promise<long> *promObj, long val, int n)
{
    // tässä ei pitäisi kirjoitella ilman mutexia, jos nyt haluaa kirjoitella :)
    m.lock();
    std::cout<<"ollaan säikeessä " << n <<std::endl;
    m.unlock();
    promObj->set_value(fibonacci(val));
}


int main()
{
    std::chrono::time_point<std::chrono::system_clock> start, end;
    start = std::chrono::system_clock::now();
   
    std::promise<long> promiseObj1;
    std::future<long> futureObj1 = promiseObj1.get_future();
    std::thread th1(initiazer, &promiseObj1, 42, 1);

    std::promise<long> promiseObj2;
    std::future<long> futureObj2 = promiseObj2.get_future();
    std::thread th2(initiazer, &promiseObj2, 42, 2);

    std::promise<long> promiseObj3;
    std::future<long> futureObj3 = promiseObj3.get_future();
    std::thread th3(initiazer, &promiseObj3, 42, 3);

    th1.join();
    th2.join();
    th3.join();

    std::cout<<futureObj1.get()<<std::endl;
    std::cout<<futureObj2.get()<<std::endl;
    std::cout<<futureObj3.get()<<std::endl;

    end = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_seconds = end-start;
    std::time_t end_time = std::chrono::system_clock::to_time_t(end);
 
    std::cout << "laskenta loppui " << std::ctime(&end_time)
              << "aikaa kului: " << elapsed_seconds.count() << " sekunttia\n";

    return 0;
}
« Viimeksi muokattu: 28.02.16 - klo:08.54 kirjoittanut teele »

kamara

  • Käyttäjä
  • Viestejä: 2952
    • Profiili
Vs: [ Ratkaistu ] c++ ja tehtävien ajastaminen
« Vastaus #6 : 28.02.16 - klo:13.27 »
Mikähän minulla menee pieleen, kun kääntäminen onnistuu, mutta ajonaikainen virhe tulee ?

Koodia: [Valitse]
$ ./fibonacciThreaded
terminate called after throwing an instance of 'std::system_error'
  what():  Enable multithreading to use std::thread: Operation not permitted
Keskeytetty (luotiin core-tiedosto)

Edit - käänsin seuraavasti...
Koodia: [Valitse]
g++ -std=c++0x -Wall fibonacciThreaded.cpp -o fibonacciThreaded

Edit2 - Google auttoi...
Koodia: [Valitse]
g++ -pthread  -std=c++0x -Wall fibonacciThreaded.cpp -o fibonacciThreaded
« Viimeksi muokattu: 28.02.16 - klo:13.30 kirjoittanut kamara »

nm

  • Käyttäjä
  • Viestejä: 16252
    • Profiili
Vs: c++ ja tehtävien ajastaminen
« Vastaus #7 : 28.02.16 - klo:14.12 »
long fibonacci( int n)

long-tyyppi voi olla 32- tai 64-bittinen alustasta riippuen. Sitä on siis paras välttää silloin, kun halutaan käyttää nimenomaan 32- tai 64-bittisiä muuttujia. Int on useimmilla moderneilla alustoilla 32-bittinen (sulautetuissa saattaa olla 16-bittinen) ja Long long on 64-bittinen. C99:ssä ja C++11:ssä voi käyttää myös int32_t ja int64_t -tyyppejä, joissa bittisyys on eksplisiittisesti selvä.

Palautusarvon tyypiksi käy siis long long tai int64_t (tai niiden etumerkittömät variantit unsigned long long ja uint64_t)

Kuten fibonacci-keskustelussa todettiin, 64-bittisen kokonaisluvun kapasiteetti loppuu jo n=93:n tienoilla, ja sen jälkeen on käytettävä monimutkaisempia esitysmuotoja. Rekursiivisella algoritmilla ei kuitenkaan päästä sinne asti käytettävissä olevan ajan ja tilan puitteissa, joten tässä erikoistapauksessa 64-bittinen tarkkuus riittää.