naghim / FFT-lab13

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Labor 13

Threadek

Qt-ban lehetőségünk adódik a processzorunkat teljes mértékben kihasználni párhuzamos szálak segítségével. Qt-ban az erre használható osztály a QThread osztály. A QThread egy absztrakt osztály, amit származtatnunk kell, és implementálnunk kell a QThread::run() függvényét. Ez lesz az a függvény, ami végrehajtásra kerül a párhuzamos szál elinditása után. Egy párhuzamos szálat a QThread::start() függvénnyel indíthatunk el. Vigyázzunk arra, hogy a threadet ne a run függvénnyel, hanem a start függvénnyel indítsuk el. Ha a run függvénnyel indítjuk, ugyanazon a szálo fog futni, mint a felhasználói felület (GUI thread).

A QThread akkor is hasznos, hogyha a háttérben szeretnénk valamit futtatni. Ha egy kódrészletet egy QThread-ben indítunk el, az egy teljesen független szálon fog futni a felhasználói felülettől. Ha egy hosszabb kódot elindítunk simán a Qt-ből, akkor le fogja fagyasztani a felületet. Ha QThread-ből indítjuk, akkor, mivel a háttérben fut egy teljesen más szálon, nem fogja befolyásolni a felület futtatását, vagyis responsive marad a felület.

Egy QThread-en belül sajnos nem tudjuk módosítani a felület kinézetét. Például nem tudunk hozzáférni a felület komponenseihez. Ha például egy progress bárt szeretnénk frissíteni, azt közvetlenül a threadből nem tudjuk. Emiatt szükséges a signal-slot mechanizmus használata. A QThread képes küldeni egy signal-t, amit kifoghatunk a felhasználói felület részéről egy megfelelő slottal. Például:

class WorkerThread : public QThread {
    Q_OBJECT

public:
    void run() override;

signals:
    void progressUpdated(int value);
};

void WorkerThread::run() {
    // Hosszabb időt felvevő kód futtatása...
    // ...

    // Frissítsük a felületet signal segítségével
    emit progressUpdated(50);
}

illetve:

class MainWindow : public QMainWindow {
    Q_OBJECT

private slots:
    void onProgressUpdated(int value);

private:
    void startThread();
};

void MainWindow::onProgressUpdated(int value) {
    // Az esemény feldolgozása
    this->progressBar->setValue(progress);
}

void MainWindow::startThread() {
    WorkerThread *thread = new WorkerThread;

    // Feliratkozás az eseményre
    connect(thread, &WorkerThread::progressUpdated, this, &MainWindow::onProgressUpdated);

    // A szál konkrét elindítása
    thread->start();
}

Feladatok

1. Készítsünk egy programot amely segítségével a felhasználó egy fájlban található számok közül meg tudja keresni a leghosszabb Collatz-pályát.

A Collatz-sejtés a következőket mondja ki: vegyünk egy tetszőleges pozitív egész számot és a következő műveleteket egymás után hajtsuk végre rajta, a végén mindig 1-hez érünk:

  • ha a szám páros, osszuk el kettővel;

  • ha a szám páratlan, háromszorozzuk meg, és adjunk hozzá egyet.

Vagyis a következő képlettel írhatjuk ezt le:

Collatz-pályának nevezzük azt a számszekvenciát, amelyet az adott számtól kiindulva, elvégezve a műveleteket kapunk egészen az 1-ig bezárólag. A lépések/számok darabszáma a pálya hossza.

Például:

7 esetén a pálya a következőképp alakul: 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1.

A szekvencia hossza pedig: 16.

A felület adjon lehetőséget a felhasználónak kiválasztani egy állományt (használjunk QFileDialog-ot), amely pozitív egész számokat tartalmaz (lásd numbers.txt a labor fájljai között). A Process gombra kattintva kapcsoljuk ki a felület gombjait, és indítsunk N darab (N a CPU magok száma) párhuzamos szálat (lásd QThread). A szálak között osszuk fel a beolvasott számokat, hogy mindegyik szál ugyanannyi számot dolgozzon fel:

int numThreads = std::thread::hardware_concurrency();

std::vector<WorkerThread *> threads;
int chunkSize = numbers.size() / numThreads;
for (int i = 0; i < numThreads; ++i) {
    int start = i * chunkSize;
    int end = (i == numThreads - 1) ? numbers.size() : start + chunkSize;
    std::vector<int> chunk(numbers.begin() + start, numbers.begin() + end);
    WorkerThread *thread = new WorkerThread(chunk);
    connect(thread, &WorkerThread::progressUpdated, this, &MainWindow::onProgressUpdated);
    connect(thread, &WorkerThread::newLongestPathFound, this, &MainWindow::onNewLongestPathFound);
    threads.push_back(thread);
}

Mindegyik szál felelős a neki kiosztott számok Collatz-pályájának kiszámításáért. Minden 100 szám kipróbálása után emitteljen egy progressUpdated nevű signált, amelyet felfog az ablakunk és frissíti a rajta lévő progress bárt. Amennyiben kapunk egy új leghosszabb Collatz-pályát értesítsük az ablakot a newLongestPathFound signállal. Ha az összes számot kipróbáltuk, akkor oldjuk fel a felületen található gombokat, hogy lehessen újrapróbálni a számítást. Vigyázzunk arra, hogy az utolsó száz vagy kevesebb számot is lejelentsük miután feldolgoztuk őket.

About