Ne zaman std :: thread :: detach kullanmalıyım?


140

Bazen std::threadbaşvurumu hızlandırmak için kullanmalıyım . Ayrıca join()bir iş parçacığı tamamlanana kadar beklediğini biliyorum . Bunu anlamak kolaydır, ancak çağırmak detach()ve çağırmamak arasındaki fark nedir?

Ben olmadan detach(), iplik yöntemi bağımsız olarak bir iplik kullanarak çalışacağını düşündüm .

Sökülmüyor:

void Someclass::Somefunction() {
    //...

    std::thread t([ ] {
        printf("thread called without detach");
    });

    //some code here
}

Ayırma ile arama:

void Someclass::Somefunction() {
    //...

    std::thread t([ ] {
        printf("thread called with detach");
    });

    t.detach();

    //some code here
}


Hem stdve boostipler gelmiş detachve joinPOSIX evreleri sonra yakından modellenmiştir.
n. 'zamirler' m.

Yanıtlar:


149

Yıkıcı std::thread, std::terminateeğer denir:

  • evre katılmamış (ile t.join())
  • ve ya (müstakil değil t.detach())

Bu nedenle, yürütme akışları yıkıcıya ulaşmadan önce her zaman ya joinda detachbir iplik geçirmelisiniz.


Bir program sona erdiğinde (yani, maingeri döndüğünde), arka planda yürütülen diğer ayrılmış iş parçacıkları beklemez; bunun yerine yürütmeleri askıya alınır ve yerel iş parçacığı nesneleri bozulur.

Bu, önemli olarak, bu ipliklerin istifinin açılmamış olduğu ve dolayısıyla bazı yıkıcıların yürütülmediği anlamına gelir. Bu yıkıcıların üstlenmesi gereken eylemlere bağlı olarak, bu program çökmüş veya öldürülmüş gibi kötü bir durum olabilir. Umarım işletim sistemi dosyalar, vb. Kilitleri serbest bırakacaktır ... ama paylaşılan bellek, yarı yazılı dosyalar ve benzeri bozulmuş olabilir.


Peki joinya kullanmalı detachmıyım?

  • kullanım join
  • Daha fazla esnekliğe ihtiyacınız olmadığı sürece VE ipliğin kendi başına tamamlanmasını beklemek için bir senkronizasyon mekanizması sağlamaya istekli değilseniz , bu durumda kullanabilirsinizdetach

Eğer pthread_exit (NULL) çağırırsanız; main () daha sonra main () 'den exit () çağrılmaz ve bu nedenle tüm ayrılmış iş parçacıkları tamamlanana kadar program yürütmeye devam eder. Sonra exit () çağrılır.
southerton

1
@Matthieu, neden std :: thread yıkıcısına katılamıyoruz?
john smith

2
@johnsmith: Mükemmel bir soru! Katıldığınızda ne olur? Konu tamamlanana kadar beklersiniz. Bir istisna atılırsa, yıkıcılar yürütülür ... ve istisna durumunuzun yayılması, iş parçacığı sonlanıncaya kadar aniden askıya alınır. Özellikle şu anda askıya alınmış iplikten girdi bekliyorsa, bunun olmamasının birçok nedeni var! Bu yüzden tasarımcılar tartışmalı bir temerrüt seçmek yerine onu açık bir seçim yapmayı seçti.
Matthieu M.

@Matthieu Ben std :: thread yıkıcı ulaşılmadan önce join () çağrı demek düşünüyorum. Kapalı bir sınıfın yıkıcısına katılabilir mi (ve katılmalı mısın?)?
Jose Quinteiro

4
@JoseQuinteiro: Aslında, diğer kaynakların aksine, tavsiye edilir değil Bir yıkıcı katılmak. Sorun yok katılmadan olmasıdır bitmez bir iş parçacığı, bu sadece bekler Bitmesini için ve yerinde bir sinyal var aksine sen ... uzun süre bekliyor olabilir sonlandırmak için iplik neden engelleme kimin yığın akım iplik açılırken ve engelliyor bu akım iplik sen olmadıkça Yani hiç ... vb bunun için bekleyen iş parçacığı engelleme böylece sonlandırma gelen belli bir zaman makul miktarda verilen bir iplik durdurabileceğini, bu beklemek en iyisidir bir yıkıcı içinde.
Matthieu M.

25

Eğer detachiş parçacığının tamamlanmasını beklemeyecekseniz, ancak iş parçacığı tamamlanıncaya joinkadar çalışmaya devam edecek ve daha sonra ana iş parçacığı için özel olarak beklemeden sonlandırılacaktır.

detachtemel olarak uygulamak için gereken kaynakları serbest bırakacaktır join.

Bir iş parçacığı nesnesinin ömrünü sonlandırması ve ne çağrılması ne joinde detachçağrılması ölümcül bir hatadır ; bu durumda terminateçağrılır.


12
Bunu, belirtmeliyim olarak fesih iplik ne edilmişse, yıkıcı denir katıldı ne de müstakil .
Nisan'da nosid

11

İpliği çıkardığınızda, join()çıkmadan önce buna gerek yoktur main().

Konu kütüphanesi aslında her tür iplik bekler aşağıda ana , ancak bunun umurumda olmamalıdır.

detach()temel olarak arka planda yapılması gereken bir görev olduğunda yararlıdır, ancak yürütülmesini umursamıyorsanız. Bu genellikle bazı kütüphaneler için geçerlidir. Onlar sessizce bir arka plan iş parçacığı oluşturmak ve ayırmak böylece fark bile olmaz.


Bu soruya cevap vermiyor. Cevap temel olarak "ayrıldığınızda ayrıldığınızı" belirtir.
rubenvb

7

Bu cevap joinve arasındaki farkı açıklamak yerine, başlıktaki soruya cevap vermeyi amaçlamaktadır detach. Peki ne zaman std::thread::detachkullanılmalı?

Düzgün bakımlı C ++ kodu std::thread::detachhiç kullanılmamalıdır. Programcı, oluşturulan tüm iş parçacıklarının, edinilen tüm kaynakları serbest bırakmadan ve diğer gerekli temizleme işlemlerini gerçekleştirerek başarıyla çıkmasını sağlamalıdır. Bu, evrimle detachiş parçacıklarının sahipliğinden vazgeçmenin bir seçenek olmadığını ve bu nedenle jointüm senaryolarda kullanılması gerektiği anlamına gelir.

Bununla birlikte, bazı uygulamalar, süresiz olarak engelleyici işlevler içerebilen eski ve genellikle iyi tasarlanmış ve desteklenmeyen API'lere dayanır. Diğer şeylerin engellenmesini önlemek için bu işlevlerin çağrılarını özel bir iş parçacığına taşımak yaygın bir uygulamadır. Böyle bir iş parçacığının zarif bir şekilde çıkmasını sağlamanın bir yolu yoktur, bu nedenle kullanımı joinsadece birincil iş parçacığı engellemesine yol açar. Bu detach, örneğin, threadnesneyi dinamik depolama süresiyle ayırmak ve daha sonra bilerek sızdırmak için daha az kötü bir alternatif olacaktır .

#include <LegacyApi.hpp>
#include <thread>

auto LegacyApiThreadEntry(void)
{
    auto result{NastyBlockingFunction()};
    // do something...
}

int main()
{
    ::std::thread legacy_api_thread{&LegacyApiThreadEntry};
    // do something...
    legacy_api_thread.detach();
    return 0;
}

1

Cppreference.com'a göre :

Yürütme iş parçacığını iş parçacığı nesnesinden ayırır ve yürütmenin bağımsız olarak devam etmesini sağlar. Ayrılan kaynaklar iş parçacığı çıktıktan sonra serbest bırakılır.

Ayrıldıktan sonra *thisartık hiçbir iş parçacığının sahibi yok.

Örneğin:

  std::thread my_thread([&](){XXXX});
  my_thread.detach();

Yerel değişken Uyarı: my_thread, yaşam boyu ise my_threadyıkıcı, bitti std::threadadı verilecek ve std::terminate()yıkıcıdaki içinde çağrılacak.

Ancak kullanırsanız detach(), my_threadartık kullanmamalısınız , ömrü my_threadbitmiş olsa bile , yeni iş parçacığına hiçbir şey olmaz.


Tamam, şimdi söylediklerimi geri alıyorum. @ TobySpeight
DinoStray

1
Not kullanırsanız o &size tarafından çevreleyen skopun değişkenleri kullanıyorsanız lambda yakalama referans - daha iyi emin herhangi Eğer referans ömrü olması bu yüzden artık iplik ömrü daha uzundur.
DavidJ
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.