Bir std :: iş parçacığının hala çalışıp çalışmadığını nasıl kontrol edebilirim?


86

A'nın std::threadhala çalışıp çalışmadığını nasıl kontrol edebilirim (platformdan bağımsız bir şekilde)? Bir timed_join()yöntemi yoktur ve joinable()bunun için tasarlanmamıştır.

Bir muteksi std::lock_guardiş parçacığı içinde kilitlemeyi ve try_lock()hala kilitli olup olmadığını (iş parçacığı çalışıyor) belirlemek için muteks yöntemini kullanmayı düşündüm , ancak bana gereksiz yere karmaşık görünüyor.

Daha zarif bir yöntem biliyor musunuz?

Güncelleme: Açık olmak gerekirse: İpliğin temiz bir şekilde çıkıp çıkmadığını kontrol etmek istiyorum. Bu amaç için "asılı" bir iplik çalıştığı kabul edilir.


Sanırım bir iş parçacığının hala çalışıp çalışmadığını kontrol etmek sadece beklediğinizde önemlidir wait()ve eğer öyleyse, wait()henüz yapmadıysanız, tanım gereği çalışıyor olmalıdır. Ancak bu mantık yanlış olabilir.
ereOn

Aslında istisnai koşullarda çıkan bir iş parçacığım var ve hala çalışıp çalışmadığını ana iş parçacığından kontrol etmek istiyorum, ancak beklemek (katılmak) istemiyorum
kispaljr

1
Koşmakla tam olarak ne demek istiyorsun? Bekleme durumunda değil, aktif olarak işleniyor mu yoksa iş parçacığının hala var olduğunu ve sona ermediğini mi söylüyorsunuz?
CashCow


4
Memnun kalmadıysan bir cevabı kabul etmemeliydin.
Nicol Bolas

Yanıtlar:


120

C ++ 11'i kullanmaya std::asyncve std::futuregörevlerinizi yürütmeye istekliyseniz , iş parçacığının hala böyle düzgün bir şekilde çalışıp çalışmadığını kontrol etmek için wait_forişlevini kullanabilirsiniz std::future:

#include <future>
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    /* Run some task on new thread. The launch policy std::launch::async
       makes sure that the task is run asynchronously on a new thread. */
    auto future = std::async(std::launch::async, [] {
        std::this_thread::sleep_for(3s);
        return 8;
    });

    // Use wait_for() with zero milliseconds to check thread status.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    auto result = future.get(); // Get result.
}

Kullanmanız gerekiyorsa, gelecekteki bir nesneyi elde etmek için std::threadkullanabilirsiniz std::promise:

#include <future>
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    // Create a promise and get its future.
    std::promise<bool> p;
    auto future = p.get_future();

    // Run some task on a new thread.
    std::thread t([&p] {
        std::this_thread::sleep_for(3s);
        p.set_value(true); // Is done atomically.
    });

    // Get thread status using wait_for as before.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    t.join(); // Join thread.
}

Bu örneklerin her ikisi de çıktı verecektir:

Thread still running

Bu tabii ki görev bitmeden önce iş parçacığı durumunun kontrol edilmesinden kaynaklanmaktadır.

Ama yine de, bunu başkalarının daha önce söylediği gibi yapmak daha kolay olabilir:

#include <thread>
#include <atomic>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    std::atomic<bool> done(false); // Use an atomic flag.

    /* Run some task on a new thread.
       Make sure to set the done flag to true when finished. */
    std::thread t([&done] {
        std::this_thread::sleep_for(3s);
        done = true;
    });

    // Print status.
    if (done) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    t.join(); // Join thread.
}

Düzenle:

Ayrıca std::packaged_taskkullanmaktan std::threaddaha temiz bir çözüm için kullanım için std::promise:

#include <future>
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    // Create a packaged_task using some task and get its future.
    std::packaged_task<void()> task([] {
        std::this_thread::sleep_for(3s);
    });
    auto future = task.get_future();

    // Run task on new thread.
    std::thread t(std::move(task));

    // Get thread status using wait_for as before.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        // ...
    }

    t.join(); // Join thread.
}

2
Güzel cevap. Ayrıca herhangi bir dönüş değeri olmadan ve gelecek <void> olmadan iş parçacıklarıyla da çalıştığını eklemek isterim
kispaljr

Bu kodun nedeni nedir std::atomic<bool> done(false);? boolVarsayılan olarak atomik değil mi?
Hi-Angel

6
@YagamyLight C ++ 'da, bir std::atomic. sizeof(bool)uygulama tanımlıdır ve> 1 olabilir, bu nedenle kısmi bir yazma gerçekleşebilir. Bir de önbellek tutarlılığı sorunu var ..
Snps


1
std :: chrono_literals'ın derlemek için C ++ 14 gerektireceğini unutmayın
Patrizio Bertoni

5

Kolay bir çözüm, iş parçacığının düzenli aralıklarla doğru olarak ayarladığı ve durumu bilmek isteyen iş parçacığı tarafından kontrol edilip yanlış olarak ayarlanan bir boole değişkenine sahip olmaktır. Değişken uzun için yanlışsa, iş parçacığı artık aktif olarak kabul edilmez.

İş parçacığı açısından daha güvenli bir yol, alt iş parçacığı tarafından artırılan bir sayaca sahip olmaktır ve ana iş parçacığı, sayacı depolanan bir değerle karşılaştırır ve çok uzun bir süre sonra aynıysa, alt iş parçacığı etkin olarak değerlendirilmez.

Bununla birlikte, C ++ 11'de asılı olan bir iş parçacığını gerçekten öldürmenin veya kaldırmanın bir yolu olmadığını unutmayın.

Düzenle Bir iş parçacığının temiz bir şekilde çıkıp çıkmadığı nasıl kontrol edilir: Temelde ilk paragrafta açıklanan teknikle aynı; Yanlış olarak başlatılmış bir boole değişkenine sahip olun. Alt iş parçacığının yaptığı son şey onu doğru olarak ayarlamaktır. Ana iş parçacığı daha sonra bu değişkeni kontrol edebilir ve eğer true ise alt iş parçacığı üzerinde çok fazla (varsa) engelleme olmadan birleştirme yapabilir.

Edit2 Eğer evre bir istisna nedeniyle çıkarsa, o zaman iki evre "main" fonksiyonuna sahip olur: İlki, içinde ikinci "gerçek" ana evre fonksiyonunu çağırdığı bir try- catchiçerir. Bu ilk ana işlev "have_exited" değişkenini ayarlar. Bunun gibi bir şey:

bool thread_done = false;

void *thread_function(void *arg)
{
    void *res = nullptr;

    try
    {
        res = real_thread_function(arg);
    }
    catch (...)
    {
    }

    thread_done = true;

    return res;
}

1
OP'nin "koşma" tanımı buysa.
CashCow

Belki beni yanlış anladın. Bir iş parçacığının temiz bir şekilde çıkıp çıkmadığını kontrol etmek istiyorum. Belirsiz ifadeler için özür dilerim.
kispaljr

7
Farklı iş parçacıkları okuyor ve yazıyorsa thread_done, bu kod bellek engeli olmadan kırılır. std::atomic<bool>Bunun yerine kullanın .
ildjarn

1
Birden fazla çalışan iş parçacığından bahsetmiyordum bool, ana iş parçacığı ondan okurken tek bir çalışan iş parçacığına atıfta bulunuyordum - bunun bir bellek engeline ihtiyacı var .
ildjarn

3
Check out bu soruyu bir neden tartışma için std::atomic<bool>buraya gereklidir.
Robert Rüger

2

Birleştirme yönteminde engellemeden bir dişin bitimini algılamak için kullanabileceğiniz bu basit mekanizma.

std::thread thread([&thread]() {
    sleep(3);
    thread.detach();
});

while(thread.joinable())
    sleep(1);

2
iş parçacığını join()ayırmak nihayetinde birinin istediği şey değildir ve eğer yapmazsanız , iş parçacığının özelliğini kaybetmesini beklemeyen bir iş parçacığından çağırmanız gerekir joinable(), aksi takdirde sonsuz döngüye girer (yani joinable()iş parçacığı gerçekten oluncaya kadar doğru döndürür) join()ed ve bitene kadar değil)
Niklas R

birleştirilebilir, bir ipliğin bir iplik sapı tuttuğu anlamına gelir. iş parçacığı yapılırsa, yine de birleştirilebilir olacaktır. iş parçacığının sonunu beklemeden kontrol etmeniz gerekiyorsa, işte çözüm. Bu sadece birkaç satırlık bir koddur. Neden önce denemedin?
Evgeny Karpov

Yaptım ve belirtmeye çalıştığım nokta şu ki, eğer thread.detach()parçayı kaldırırsanız, yukarıdaki program asla sona ermeyecek.
Niklas R

evet, olmayacak. bu yüzden sonunda ayrılık diyor.
Evgeny Karpov

1
Detach'ı çağırmanın bu yöntemi, muteks ve diğer daha karmaşık çözümlere olan ihtiyacı ortadan kaldırır. Ben kullanıyorum ve işe yarıyor! Cevap için teşekkürler.
Eylül

1

Çalışan iş parçacığının ve çağıran iş parçacığının her ikisinin de erişebildiği bir muteks oluşturun. Çalışan iş parçacığı başladığında, muteksi kilitler ve sona erdiğinde muteksin kilidini açar. Evrenin hala çalışıp çalışmadığını kontrol etmek için, çağıran evre mutex.try_lock () 'u çağırır. Bunun dönüş değeri, iş parçacığının durumudur. (Try_lock çalıştıysa muteksin kilidini açtığınızdan emin olun)

Bununla ilgili küçük bir sorun olan mutex.try_lock (), iş parçacığının oluşturulduğu zaman ile muteksi kilitlediği zaman arasında false döndürür, ancak bu biraz daha karmaşık bir yöntem kullanılarak önlenebilir.


-1 std::mutexBu tür bir sinyalleme için kullanmamalısınız (çoğunlukla muteksin nasıl uygulandığına bağlı olarak). An atomic_flag, bu durumda çok daha az masrafla işe yarar. A std::future, niyeti daha net ifade ettiği için daha da iyi olabilir. Ayrıca, bunun try_locksahte bir şekilde başarısız olabileceğini unutmayın , bu nedenle dönüş mutlaka iş parçacığının durumu değildir (bu özel durumda muhtemelen size çok fazla zarar vermeyecek olsa da).
ComicSansMS

1

Her zaman evre kimliğinin std :: thread :: id () varsayılan yapılandırılandan farklı olup olmadığını kontrol edebilirsiniz. Bir Çalışan iş parçacığı her zaman gerçek bir ilişkilendirilmiş kimliğe sahiptir. Çok fazla süslü şeyden kaçınmaya çalışın :)


0

Elbette, çıkıştan önce yaptığı son şey olarak falseiş parçacığının ayarladığı, muteks-sarılmış bir değişken başlatılmıştır true. Bu atomik ihtiyaçlarınız için yeterli mi?


1
Yine de bir muteks kullanırsanız, çözümümün (boole olmadan sadece muteks kullanarak) daha zarif olduğunu hissediyorum. Eğer iş parçacığı güvenli bir mantıksal kullanmak istiyorsanız, bunun yerine std :: atomic <bool> 'u öneririm. Çoğu uygulamada kilitsiz olacaktır.
kispaljr

Neden kilitleniyor? Bir ileti dizisi yalnızca okur, yalnızca biri yazar. Ve kelime boyutunda yazılar her durumda IIRC atomiktir.
Xeo

1
@Xeo: Yazma atomik olabilir, ancak yazılı değeri farklı bir iş parçacığında görmeyi bekliyorsanız (farklı bir CPU'da çalışıyor olabilir) yine de bir bellek engeli gereklidir. std::atomic<bool>bu yüzden, bu sizin için ilgilenir bu kadar gerçek cevabı IMO.
ildjarn
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.