Aktif bir istisna olmadan çağrılan C ++ sonlandırması


94

Diş açarken bir C ++ hatası alıyorum:

terminate called without an active exception
Aborted

İşte kod:

#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

template<typename TYPE>
class blocking_stream
{
public:
    blocking_stream(size_t max_buffer_size_)
        :   max_buffer_size(max_buffer_size_)   
    {
    }

    //PUSH data into the buffer
    blocking_stream &operator<<(TYPE &other)
    {
        std::unique_lock<std::mutex> mtx_lock(mtx); 
        while(buffer.size()>=max_buffer_size)
            stop_if_full.wait(mtx_lock);

        buffer.push(std::move(other));

        mtx_lock.unlock();
        stop_if_empty.notify_one();
        return *this;
    }
    //POP data out of the buffer 
    blocking_stream &operator>>(TYPE &other)
    {
        std::unique_lock<std::mutex> mtx_lock(mtx);
        while(buffer.empty())
            stop_if_empty.wait(mtx_lock);

        other.swap(buffer.front()); 
        buffer.pop();

        mtx_lock.unlock();
        stop_if_full.notify_one();
        return *this;
    }

private:
    size_t max_buffer_size;
    std::queue<TYPE> buffer;
    std::mutex mtx;
    std::condition_variable stop_if_empty,
                            stop_if_full;
    bool eof;   
};

Kodumu şu örnek etrafında modelledim: http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html

Neyi yanlış yapıyorum ve hatayı nasıl düzeltirim?


9
joinAna programınızdaki tüm konu başlıklarını mı işliyorsunuz?
Kerrek SB

Bize kodun geri kalanını göster.
Matt

2
@Kerrek ah ha bu sorunu çözdü, işçiler bitmeden ana iş parçacığının sona ermediğinden neden emin olsam da hiçbir fikrim yok. Ayrıca kilitleme algoritmalarım doğru görünüyor mu?
111111

Lütfen sorunu yeniden üreten derlenebilir kod.
Martin York

3
Bu durumda çalışma zamanı daha iyi bir teşhis verebilir gibi görünüyor?
Nemo

Yanıtlar:


127

Bir iş parçacığı nesnesi kapsam dışına çıktığında ve birleştirilebilir durumda olduğunda, program sonlandırılır. Standart Komite'nin birleştirilebilir bir iş parçacığının yıkıcısı için iki seçeneği daha vardı. Sessizce katılabilir - ancak iş parçacığı sıkışmışsa birleştirme asla geri dönmeyebilir. Veya ipliği ayırabilir (ayrılmış bir diş birleştirilemez). Bununla birlikte, ayrılmış iş parçacıkları, programın sonuna kadar hayatta kalabilecekleri ve kaynakların serbest bırakılmasını bozabilecekleri için çok zordur. Bu nedenle, programınızı sonlandırmak istemiyorsanız, her iş parçacığına katıldığınızdan (veya ayırdığınızdan) emin olun.


1
"Bir iş parçacığı nesnesi kapsam dışına çıktığında ve birleştirilebilir durumda olduğunda, program sonlandırılır" Bunun son derece basit, tekrarlanabilir bir örneğini verebilir misiniz? OP'deki örnek biraz karmaşıktır.
Alec Jacobson

1
Ve bu ifade, bu yanıtla çelişkili görünüyor: stackoverflow.com/a/3970921/148668
Alec Jacobson

5
@mangledorf: Dikkat edin aobut boost :: thread ve ben std :: thread'den bahsediyorum. Bu ikisinin farklı yıkım davranışları vardır. Bu, Komite adına bilinçli bir karardı.
Bartosz Milewski

Ya bu sorunla karşılaşırsanız std::async? Orada oluşturulabilecek herhangi bir iş parçacığını nasıl birleştirir / çıkarırsınız? Ortaya çıkan geleceği beklemek yeterli gibi görünmüyor, çünkü bu , iş parçacığının "potansiyel olarak bir iş parçacığı havuzundan" olabileceğini söylüyor ve geleceğin wait () bir havuzdaki bir iş parçacığını gerçekten sonlandırmak anlamına gelmiyor (ve bu Aklı başında iş parçacığı havuzları için zaten mantıklı değil).
Jason C

2
Sadece C ++ 20'de std::jthreadyıkıcıyı çağıracak bir güncelleme .join()(kapsam dışına çıktıkça). RAII'yi daha iyi takip ettiği için şahsen daha çok sevdiğim.
pooya13

46

Bu hata nasıl yeniden oluşturulur:

#include <iostream>
#include <stdlib.h>
#include <string>
#include <thread>
using namespace std;
void task1(std::string msg){
  cout << "task1 says: " << msg;
}
int main() { 
  std::thread t1(task1, "hello"); 
  return 0;
}

Derleyin ve çalıştırın:

el@defiant ~/foo4/39_threading $ g++ -o s s.cpp -pthread -std=c++11
el@defiant ~/foo4/39_threading $ ./s
terminate called without an active exception
Aborted (core dumped)

Bu hatayı, iş parçacığına katılmadığınız veya çıkarmadığınız için alıyorsunuz.

Düzeltmenin bir yolu, ileti dizisine şu şekilde katılın:

#include <iostream>
#include <stdlib.h>
#include <string>
#include <thread>
using namespace std;
void task1(std::string msg){
  cout << "task1 says: " << msg;
}
int main() { 
  std::thread t1(task1, "hello"); 
  t1.join();
  return 0;
}

Ardından derleyin ve çalıştırın:

el@defiant ~/foo4/39_threading $ g++ -o s s.cpp -pthread -std=c++11
el@defiant ~/foo4/39_threading $ ./s
task1 says: hello

Düzeltmenin diğer yolu, şu şekilde ayırın:

#include <iostream>
#include <stdlib.h>
#include <string>
#include <unistd.h>
#include <thread>
using namespace std;
void task1(std::string msg){
  cout << "task1 says: " << msg;
}
int main() 
{ 
     {

        std::thread t1(task1, "hello"); 
        t1.detach();

     } //thread handle is destroyed here, as goes out of scope!

     usleep(1000000); //wait so that hello can be printed.
}

Derleyin ve çalıştırın:

el@defiant ~/foo4/39_threading $ g++ -o s s.cpp -pthread -std=c++11
el@defiant ~/foo4/39_threading $ ./s
task1 says: hello

C ++ iş parçacıklarını ayırma ve C ++ iş parçacıklarına katılma hakkında bilgi edinin.


1
bu bağlamda, usleep () kullanımı yalnızca evre ayrılmışsa ve tutamaç yok edilmişse (kapsam dışına çıkarak) anlamlıdır. Bu yüzden kodunuzu bunu yansıtacak şekilde düzenledim.
Nawaz

17

Eric Leschinski ve Bartosz Milewski cevabı şimdiden verdiler. Burada daha başlangıç ​​dostu bir şekilde sunmaya çalışacağım.

Bir iş parçacığı bir kapsam içinde başlatıldığında (kendisi bir iş parçacığı üzerinde çalışıyor), iş parçacığı kapsam dışına çıkmadan önce aşağıdakilerden birinin gerçekleştiğinden emin olunmalıdır:

  • Çalışma zamanı, yalnızca bu iş parçacığı yürütmeyi bitirdikten sonra kapsamdan çıkar. Bu, o iş parçacığı ile birleştirilerek elde edilir. Dili not edin, bu konu ile birleşen dış kapsamdır.
  • Çalışma zamanı, iş parçacığını kendi başına çalışmak üzere bırakır. Böylece, bu iş parçacığının yürütülmesini bitirip bitirmediğine bakılmaksızın program kapsamdan çıkacaktır. Bu iş parçacığı kendi kendine yürütülür ve çıkar. Bu, ipliğin çıkarılmasıyla elde edilir. Bu, örneğin iş parçacığı dış kapsamdaki değişkenlere atıfta bulunuyorsa sorunlara yol açabilir.

İş parçacığı birleştirildiğinde veya ayrıldığında, yürütmeyi iyi bitirmiş olabilir. Yine de iki işlemden biri açıkça gerçekleştirilmelidir.


1

Programınız öldüğü sürece, iş parçacığı ayrılmadan veya birleştirilmeden bu hata ortaya çıkacaktır. İpliği ayırmadan ve birleştirmeden, iplik oluşturduktan sonra sonsuz döngü vermelisiniz.

int main(){

std::thread t(thread,1);

while(1){}

//t.detach();
return 0;}

Ayrıca ilginçtir ki, uyuduktan veya döngü yaptıktan sonra ipliğin ayrılması veya birleştirilmesi. Ayrıca bu şekilde bu hatayı almazsınız.

Aşağıdaki örnek, üçüncü ipliğin ana kalıptan önce işini yapamayacağını da göstermektedir. Ancak, kodun herhangi bir yerinden ayrıldığınız sürece bu hata da gerçekleşemez. Üçüncü iş parçacığı 8 saniye uyur ancak ana işlem 5 saniye içinde ölecektir.

void thread(int n) {std::this_thread::sleep_for (std::chrono::seconds(n));}

int main() {
std::cout << "Start main\n";
std::thread t(thread,1);
std::thread t2(thread,3);
std::thread t3(thread,8);
sleep(5);

t.detach();
t2.detach();
t3.detach();
return 0;}

1

yıl, iş parçacığı join () olmalıdır. ana çıkış ne zaman


1
Bu cevap, muhtemelen başka bir cevaba eklenmiş bir yoruma daha uygundur. Ve söylemeliyim ki Stack Overflow'a hoş geldiniz!
Contango

0

Önce bir iş parçacığı tanımlarsınız. Ve eğer evre yıkıcıyı çağırmadan önce join () veya detach () 'ı asla çağırmazsanız, program durdurulacaktır.

Aşağıdaki gibi, önce join (bitmesini beklemek için) veya detach olmadan evre yıkıcı çağırmak, std :: terminate'i hemen çağırmak ve programı sonlandırmak için garantilidir.

Bir birleştirilebilir () iş parçacığının yıkıcıda örtük olarak ayrılması veya birleştirilmesi, yalnızca bir istisna ortaya çıktığında karşılaşılan hataların ayıklanmasında güçlük (ayırma için) veya performans (birleştirme için) hatalarıyla sonuçlanabilir. Bu nedenle programcı, evre hala birleştirilebilir durumdayken yıkıcının asla çalıştırılmamasını sağlamalıdır.

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.