C ++ kết thúc được gọi mà không có ngoại lệ hoạt động


94

Tôi đang gặp lỗi C ++ với luồng:

terminate called without an active exception
Aborted

Đây là mã:

#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;   
};

Tôi đã lập mô hình mã của mình xung quanh ví dụ này: http://www.justsoftwaresolutions.co.uk/threading/implecting-a-thread-safe-queue-using-condition-variables.html

Tôi đang làm gì sai và làm cách nào để sửa lỗi?


9
Bạn có nhập jointất cả các chủ đề của mình vào chương trình chính của bạn không?
Kerrek SB

Cho chúng tôi xem phần còn lại của mã.
Matt

2
@Kerrek ah ha điều này đã khắc phục sự cố, tôi không biết tại sao mặc dù tôi chắc chắn rằng luồng chính đã không kết thúc trước khi các nhân viên kết thúc. Ngoài ra, nhịp điệu khóa của tôi trông có đúng không?
111111

Xin vui lòng mã tổng hợp tái tạo sự cố.
Martin York

3
Có vẻ như thời gian chạy có thể đưa ra chẩn đoán tốt hơn trong trường hợp này?
Nemo

Câu trả lời:


127

Khi một đối tượng luồng đi ra khỏi phạm vi và nó ở trạng thái có thể nối, chương trình sẽ bị kết thúc. Ủy ban Tiêu chuẩn có hai lựa chọn khác cho trình hủy của một chuỗi có thể kết hợp. Nó có thể lặng lẽ tham gia - nhưng tham gia có thể không bao giờ quay lại nếu chuỗi bị kẹt. Hoặc nó có thể tách chuỗi (một chuỗi tách ra không thể nối được). Tuy nhiên, các luồng tách rời rất phức tạp, vì chúng có thể tồn tại cho đến cuối chương trình và làm rối tung việc giải phóng tài nguyên. Vì vậy, nếu bạn không muốn chấm dứt chương trình của mình, hãy đảm bảo bạn tham gia (hoặc tách) mọi chuỗi.


1
"Khi một đối tượng luồng vượt ra khỏi phạm vi và nó ở trạng thái có thể kết hợp, chương trình sẽ bị kết thúc" Bạn có thể cung cấp một ví dụ đơn giản, có thể tái tạo được về điều này không? Ví dụ trong OP hơi phức tạp.
Alec Jacobson

1
Và tuyên bố rằng dường như mâu thuẫn với câu trả lời này: stackoverflow.com/a/3970921/148668
Alec Jacobson

5
@mangledorf: Lưu ý rằng họ đang nói về aobut boost :: thread và tôi đang nói về std :: thread. Hai người này có hành vi phá hủy khác nhau. Đây là một quyết định có ý thức từ phía Ủy ban.
Bartosz Milewski 20/09/12

Điều gì sẽ xảy ra nếu bạn gặp phải vấn đề này với std::async? Làm thế nào để bạn tham gia / tách bất kỳ chuỗi nào có thể được tạo ở đó? Có vẻ như việc chờ đợi vào tương lai kết quả là chưa đủ, vì điều này nói rằng chuỗi có thể "có khả năng là từ một nhóm chuỗi" và chờ đợi trong tương lai () không thực sự ngụ ý kết thúc một chuỗi trong một nhóm (và điều đó sẽ không 'không có ý nghĩa gì cho các nhóm chủ đề lành mạnh).
Jason C

2
Chỉ một bản cập nhật trong C ++ 20, std::jthreadsẽ gọi .join()đến hàm hủy (vì nó nằm ngoài phạm vi). Cá nhân tôi thích cái nào hơn vì nó theo RAII tốt hơn.
pooya13

46

Cách tạo lại lỗi đó:

#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;
}

Biên dịch và chạy:

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)

Bạn gặp lỗi đó vì bạn không tham gia hoặc tách chuỗi của mình.

Một cách để khắc phục nó, hãy tham gia chuỗi như thế này:

#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;
}

Sau đó biên dịch và chạy:

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

Cách khác để sửa nó, hãy tách nó ra như thế này:

#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.
}

Biên dịch và chạy:

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

Đọc thêm về tách các chuỗi C ++ và nối các chuỗi C ++.


1
trong ngữ cảnh này, việc sử dụng usleep () chỉ có ý nghĩa khi luồng bị tách ra và xử lý đã bị phá hủy (bằng cách ra khỏi phạm vi). VẬY, tôi đã chỉnh sửa mã của bạn để phản ánh điều này.
Nawaz

17

Eric Leschinski và Bartosz Milewski đã đưa ra câu trả lời. Ở đây, tôi sẽ cố gắng trình bày nó một cách thân thiện hơn cho người mới bắt đầu.

Khi một tiểu trình đã được bắt đầu trong một phạm vi (chính nó đang chạy trên một tiểu trình), người ta phải đảm bảo một cách rõ ràng một trong những điều sau đây xảy ra trước khi tiểu trình đó ra khỏi phạm vi:

  • Thời gian chạy thoát khỏi phạm vi, chỉ sau khi luồng đó kết thúc thực thi. Điều này đạt được bằng cách tham gia với chuỗi đó. Lưu ý ngôn ngữ, nó là phạm vi bên ngoài tham gia với luồng đó.
  • Thời gian chạy để luồng tự chạy. Vì vậy, chương trình sẽ thoát khỏi phạm vi, cho dù luồng này đã thực thi xong hay chưa. Luồng này tự thực thi và thoát. Điều này đạt được bằng cách tách sợi. Điều này có thể dẫn đến các vấn đề, ví dụ, nếu chuỗi tham chiếu đến các biến trong phạm vi bên ngoài đó.

Lưu ý, vào thời điểm chuỗi được nối với hoặc tách ra, nó có thể đã hoàn tất quá trình thực thi. Vẫn phải thực hiện một trong hai thao tác một cách rõ ràng.


1

Miễn là chương trình của bạn chết, sau đó không tách hoặc nối chuỗi, lỗi này sẽ xảy ra. Không tách rời và nối chuỗi, bạn nên tạo vòng lặp vô tận sau khi tạo chuỗi.

int main(){

std::thread t(thread,1);

while(1){}

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

Điều thú vị là, sau khi ngủ hoặc lặp lại, luồng có thể được tách ra hoặc tham gia. Ngoài ra với cách này bạn không gặp lỗi này.

Ví dụ dưới đây cũng cho thấy rằng, luồng thứ ba không thể thực hiện công việc của mình trước khi main chết. Nhưng lỗi này cũng không thể xảy ra, miễn là bạn tách một nơi nào đó trong mã. Luồng thứ ba ngủ trong 8 giây nhưng chính sẽ chết sau 5 giây.

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

năm, chủ đề phải là join (). khi lối ra chính


1
Câu trả lời này có lẽ phù hợp hơn với một nhận xét đính kèm với một câu trả lời khác. Và tôi phải nói rằng, chào mừng bạn đến với Stack Overflow!
Contango

0

Đầu tiên, bạn xác định một chủ đề. Và nếu bạn không bao giờ gọi tham gia () hoặc detach () trước khi gọi trình hủy luồng, chương trình sẽ hủy bỏ.

Như sau, việc gọi một trình hủy luồng mà không cần gọi tham gia trước (để đợi nó kết thúc) hoặc tách ra được yêu cầu ngay lập tức gọi std :: chấm dứt và kết thúc chương trình.

Việc tách hoặc tham gia ngầm một luồng có thể ghép nối () trong trình hủy của nó có thể dẫn đến việc khó gỡ lỗi về tính đúng đắn (đối với tách) hoặc lỗi hiệu suất (đối với tham gia) chỉ gặp phải khi một ngoại lệ được đưa ra. Do đó, người lập trình phải đảm bảo rằng hàm hủy không bao giờ được thực thi trong khi luồng vẫn có thể nối.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.