Saya memiliki kelas yang memulai utas lain yang mengakses beberapa datanya pada interval yang konstan. Ini berarti saya memiliki dua utas yang mengakses data yang sama (utas asli dan utas yang baru dibuat). Ini memperkenalkan kebutuhan akan mutex. Semua berjalan dengan baik sampai destruktor kelas dipanggil (di akhir program) dan lokasi memori tidak lagi valid. Pada titik ini utas baru mencoba mengakses data dan mendapatkan kesalahan pelanggaran akses (jelas).

Yang ingin saya lakukan adalah menghentikan utas di destruktor, atau menghentikan utas setelah "memperhatikan" bahwa instance kelas telah dihancurkan.

Berikut adalah kode utas yang disederhanakan (typedefs digunakan untuk singkatnya):

void myClass::StartThread() {
    auto threadFunc = [&, this]() {
        while (true) {
            time_point now = steady_clock::now();
            if (chro::duration_cast<chro::milliseconds>(now - this->m_lastSeedTime).count() > INTERVAL) {
                std::lock_guard<std::mutex> lockGuard(this->m_mut);
                this->m_lastSeedTime = now;
                this->accessData();
            }
        }
    };
    std::thread thread(threadFunc);
    thread.detach();

Tentu saja jika saya hanya salah menangani ini dengan cara yang jelas, beri tahu saya juga.

1
fishrec 23 Desember 2020, 20:21

2 jawaban

Jawaban @Useless' benar. Inilah cara tepatnya Anda dapat melakukannya:

class myClass{
    ...
private:
    std::thread m_thread;
    std::atomic_bool m_keepRunning{true};
    ....
};


void myClass::StartThread() {
    auto threadFunc = [&, this]() {
        while (m_keepRunning) {
            time_point now = steady_clock::now();
            if (chro::duration_cast<chro::milliseconds>(now - this->m_lastSeedTime).count() > INTERVAL) {
                std::lock_guard<std::mutex> lockGuard(this->m_mut);
                if(!m_keepRunning) break; // destructor called, don't access data
                this->m_lastSeedTime = now;
                this->accessData();
            }
        }
    };
    m_thread = std::thread(threadFunc);
}

myClass::~myClass()
{
    m_keepRunning = false;
    m_mutex.unlock(); // make sure we don't wait in the loop for the lock
    if(m_thread.joinable()) m_thread.join();
    
    // do other cleaning
}

Poin lainnya adalah, ketika Anda selalu menunggu INTERVAL, itu akan menyebabkan penundaan kumulatif dalam waktu. Katakanlah interval Anda adalah 50 ms. Ketika CPU Anda memiliki terlalu banyak pekerjaan yang harus dilakukan atau fungsi accessData membutuhkan terlalu banyak waktu, Anda tidak akan dapat menjalankan iterasi berikutnya tepat dalam 50 md. Katakanlah itu akan menjadi 52 mdtk, yang merupakan penundaan 2 mdtk. Penundaan ini akan bertambah dalam waktu dan akan mempengaruhi presisi Anda.

Sebagai gantinya, Anda dapat melakukan:

time_point waitUntil = steady_clock::now() + initialWaitTime;
while(m_keepRunning){
    if(steady_clock::now() >= waitUntil)
    {
        // ... do your work
        waitUntil = waitUntil + chro::milliseconds(INTERVAL)
    }
}

Juga @Useless benar lagi untuk bagian menunggu waktunya. Berputar akan menyebabkan beban berat pada inti Anda. Sebagai gantinya, Anda harus menggunakan conditional atau timed_mutex. Tapi saran di atas masih berlaku. Alih-alih menggunakan sleep_for, pilih yang sleep_until.

2
seleciii44 23 Desember 2020, 18:30

Membunuh utas tidak berfungsi. Masalahnya adalah jika Anda mematikan utas, itu bisa berada di tengah-tengah operasi beberapa langkah yang harus dilakukan sebagai operasi atom, meninggalkan program Anda dalam keadaan tidak valid. Sebagai gantinya, beri tanda pada utas lainnya untuk bunuh diri, dan tunggu sampai mati.

1
Raedwald 23 Desember 2020, 17:28