Có thông minh không khi thay thế boost :: thread và boost :: mutex bằng c ++ 11 tương đương?


153

Động lực: lý do tại sao tôi xem xét rằng người quản lý dự án thiên tài của tôi nghĩ rằng tăng là một sự phụ thuộc khác và nó thật kinh khủng vì "bạn phụ thuộc vào nó" (Tôi đã cố gắng giải thích chất lượng của tăng, sau đó đã bỏ cuộc sau một thời gian :( ). Lý do nhỏ hơn tại sao tôi muốn làm điều đó là tôi muốn tìm hiểu các tính năng của c ++ 11, vì mọi người sẽ bắt đầu viết mã trong đó. Vì vậy:

  1. Có ánh xạ 1: 1 giữa #include<thread> #include<mutex>và tăng tương đương không?
  2. Bạn có muốn xem xét một ý tưởng tốt để thay thế công cụ boost bằng công cụ c ++ 11 không
    . Cách sử dụng của tôi là nguyên thủy, nhưng có những ví dụ khi std không cung cấp những gì boost không? Hoặc (báng bổ) ngược lại?

PS tôi sử dụng GCC để tiêu đề ở đó.


46
IMO Google hướng dẫn mã hóa là ngu ngốc theo nhiều cách ... Ví dụ. họ không cho phép tự động từ C ++ 11 ... :)
NoSenseEtAl 15/03/2016

5
Hướng dẫn trích dẫn: [auto] cản trở khả năng đọc [vì nó loại bỏ] sự dư thừa đã kiểm tra (như tên loại) có thể hữu ích cho người đọc.
Andrew Tomazos

31
cho (tự động nó = v.begin () ... :)
NoSenseEtAl

15
@ AndrewTomazos-Fathomling: Thật sao? Cá nhân tôi không nghĩ rằng tôi đã từng quan tâm đến loại trình lặp thực tế (cũng có thể là một vài lần), chỉ các hoạt động được hỗ trợ ... Tôi sẽ cho rằng dự phòng cú pháp hiếm khi là một suy nghĩ tốt (DRY).
Grizzly

16
btw google đã sửa đổi các nguyên tắc ngu ngốc của mình để bây giờ cuối cùng họ cho phép tự động
NoSenseEtAl

Câu trả lời:


192

Có một số khác biệt giữa Boost.Thread và thư viện luồng tiêu chuẩn C ++ 11:

  • Boost hỗ trợ hủy chủ đề, chủ đề C ++ 11 thì không
  • C ++ 11 hỗ trợ std::async, nhưng Boost thì không
  • Boost có khóa boost::shared_mutexdành cho nhiều người đọc / người viết đơn. Sự tương tự std::shared_timed_mutexchỉ có sẵn kể từ C ++ 14 ( N3891 ), trong khi std::shared_mutexchỉ có sẵn kể từ C ++ 17 ( N4508 ).
  • Thời gian chờ của C ++ 11 khác với thời gian chờ Boost (mặc dù điều này sẽ sớm thay đổi ngay bây giờ Boost.Chrono đã được chấp nhận).
  • Một số tên khác nhau (ví dụ boost::unique_futurevs std::future)
  • Các ngữ nghĩa truyền đối số std::threadkhác với boost::thread--- Sử dụng Boost boost::bind, đòi hỏi các đối số có thể sao chép. std::threadcho phép các kiểu chỉ di chuyển như std::unique_ptrđược truyền dưới dạng đối số. Do việc sử dụng boost::bind, ngữ nghĩa của các trình giữ chỗ như _1trong các biểu thức liên kết lồng nhau cũng có thể khác nhau.
  • Nếu bạn không gọi một cách rõ ràng join()hoặc detach()sau đó boost::threadtoán tử hủy và toán tử gán sẽ gọi detach()đối tượng luồng bị hủy / gán cho. Với một std::threadđối tượng C ++ 11 , điều này sẽ dẫn đến một cuộc gọi đến std::terminate()và hủy bỏ ứng dụng.

Để làm rõ quan điểm về các tham số chỉ di chuyển, sau đây là C ++ 11 hợp lệ và chuyển quyền sở hữu inttừ tạm thời std::unique_ptrsang tham số f1khi luồng mới được bắt đầu. Tuy nhiên, nếu bạn sử dụng boost::threadthì nó sẽ không hoạt động, vì nó sử dụng boost::bindnội bộ và std::unique_ptrkhông thể sao chép. Ngoài ra còn có một lỗi trong thư viện luồng C ++ 11 được cung cấp với GCC để ngăn việc này hoạt động, vì nó cũng sử dụng std::bindtrong quá trình thực hiện ở đó.

void f1(std::unique_ptr<int>);
std::thread t1(f1,std::unique_ptr<int>(new int(42)));

Nếu bạn đang sử dụng Boost thì có lẽ bạn có thể chuyển sang các luồng C ++ 11 tương đối dễ dàng nếu trình biên dịch của bạn hỗ trợ nó (ví dụ: các phiên bản gần đây của GCC trên linux có triển khai hầu hết thư viện luồng C ++ 11 trong -std=c++0xchế độ).

Nếu trình biên dịch của bạn không hỗ trợ các luồng C ++ 11 thì bạn có thể có được triển khai của bên thứ ba như Just :: Thread , nhưng đây vẫn là một phụ thuộc.


1
Có các phương pháp khóa / mở khóa riêng cho người đọc và người viết ( lock/ unlockcho người viết so với 'lock_ Shared / Unlock_ Shared' cho người đọc). Nhiều người đọc có thể gọi lock_ Shared mà không chặn, miễn là không có nhà văn nào đang sử dụng nó.
Dave S

2
Các shared_mutextài liệu có tại boost.org/doc/libs/1_47_0/doc/html/thread/ . Bạn có thể khóa mutex dưới dạng chia sẻ hoặc độc quyền, sau đó sử dụng chức năng mở khóa tương ứng. Bạn cũng có thể sử dụng các loại RAII để thực hiện việc này ( shared_locklấy một khóa đọc được chia sẻ lock_guardunique_locklấy một khóa độc quyền). Tôi đã cố gắng làm rõ quan điểm về các loại chỉ di chuyển.
Anthony Williams

3
Một điều nữa mà nhỏ vấp tôi lên: trong thúc đẩy, destructor của một thread chạy tách nó ( boost.org/doc/libs/1_47_0/doc/html/thread/... ), trong khi trong C ++, destructor của một cuộc gọi chạy chủ đề chấm dứt () (FDIS 30.3.1.3)
Cubbi

3
Trong C ++ 11, try_scoped_lockchức năng được bao phủ bởi std::unique_lock. Có một hàm tạo có một mutex và std::try_to_locksau đó sẽ gọi try_lock()mutex chứ không phải lock(). Xem stdthread.co.uk/doc/headers/mutex/unique_lock/ory
Anthony Williams

4
Vâng, Boost.Thread đã trở nên gần gũi hơn với tiêu chuẩn C ++ 11 kể từ khi tôi viết bài này, chủ yếu là do công việc của Vicente Botet.
Anthony Williams

24

std::threadphần lớn được mô hình hóa sau boost::thread, với một vài khác biệt :

  • chủ đề không thể sao chép, xử lý một bản đồ-thành-một-os-chủ đề, ngữ nghĩa được giữ lại. Nhưng luồng này có thể di chuyển để cho phép trả lại luồng từ các chức năng của nhà máy và đặt vào các thùng chứa.
  • Đề xuất này thêm hủy bỏ boost::thread, đó là một biến chứng đáng kể. Sự thay đổi này có tác động lớn không chỉ đối với luồng mà cả phần còn lại của thư viện luồng C ++. Người ta tin rằng sự thay đổi lớn này là chính đáng vì lợi ích.
    • Bộ hủy luồng bây giờ phải gọi hủy trước khi tách ra để tránh vô tình rò rỉ các luồng con khi các luồng gốc bị hủy.
    • Một thành viên tách biệt rõ ràng hiện được yêu cầu để cho phép tách ra mà không hủy bỏ.
  • Các khái niệm về xử lý luồng và nhận dạng luồng đã được tách thành hai lớp (chúng là cùng một lớp boost::thread). Điều này là để hỗ trợ thao tác dễ dàng hơn và lưu trữ danh tính chủ đề.
  • Khả năng tạo id luồng được đảm bảo để so sánh bằng với không có luồng có thể nối nào khác được thêm vào ( boost::threadkhông có điều này). Điều này rất hữu ích cho mã muốn biết liệu nó có được thực hiện bởi cùng một luồng như một cuộc gọi trước đó không (các biến đổi đệ quy là một ví dụ cụ thể).
  • Tồn tại một "cửa sau" để xử lý luồng gốc để khách hàng có thể thao tác các luồng bằng HĐH cơ bản nếu muốn.

Đây là từ năm 2007, vì vậy một số điểm không còn hợp lệ: hiện boost::threadnative_handlechức năng và, như các nhà bình luận chỉ ra, std::threadkhông còn hủy bỏ nữa.

Tôi không thể tìm thấy bất kỳ sự khác biệt đáng kể giữa boost::mutexstd::mutex.


2
std::threadkhông hủy bỏ; nó là boost::threadvậy
Anthony Williams

@Anthony bạn có chắc là bạn không có ý nghĩa interrupt()cho boost :: thread không? Ngoài ra, có vẻ như đó là một đề xuất ban đầu, đã thay đổi từ năm 2007
Alex B

4
Có, hủy bỏ trong boost được gọi là "gián đoạn". Vâng, đây là một đề xuất cũ. Bản nháp công khai mới nhất của tiêu chuẩn C ++ 11 (bao gồm thư viện luồng) là open-std.org/jtc1/sc22/wg21/docs/ con / 2011 / n3242.pdf
Anthony Williams

6

Có một lý do để không di chuyển đến std::thread.

Nếu bạn đang sử dụng liên kết tĩnh, std::threadsẽ không sử dụng được do các lỗi / tính năng gcc này:

Cụ thể, nếu bạn gọi std::thread::detachhoặc std::thread::joinnó sẽ dẫn đến ngoại lệ hoặc sự cố, trong khi boost::threadhoạt động tốt trong những trường hợp này.


Tôi thấy rằng một lỗi là UNCONFIRMED và lỗi còn lại là HÓA, với một bình luận nói rằng phóng viên nên liên kết với libpthread.a. Bạn có chắc chắn trong những gì bạn đang nói?
einpoklum

1
@einpoklum, bạn sẽ có thể làm cho nó hoạt động bằng cách sử dụng Wl,--whole-archive -lpthread -Wl,--no-whole-archive, xem câu trả lời này ví dụ stackoverflow.com/a/23504509/72178 . Nhưng nó không phải là cách rất đơn giản để liên kết với libpthread.avà cũng được coi là ý tưởng tồi.
ks1322

4
Chúng ta có thể giả sử những lỗi này đã được sửa chữa như bây giờ là năm 2016 không? Các lỗi đã được đăng vào năm 2012 và từ gcc 4.9.2, nó chính thức hỗ trợ C ++ 11 vì vậy chúng tôi không thể khiếu nại C ++ 11 trước khi có hỗ trợ chính thức.
Splash

6

Trường hợp doanh nghiệp

Nếu bạn đang viết phần mềm cho doanh nghiệp cần chạy trên nhiều hệ điều hành từ trung bình đến lớn và do đó xây dựng với nhiều trình biên dịch và phiên bản trình biên dịch (đặc biệt là các phiên bản tương đối cũ) trên các hệ điều hành đó, tôi khuyên bạn nên tránh xa C ++ 11 hoàn toàn cho bây giờ. Điều đó có nghĩa là bạn không thể sử dụng std::threadvà tôi khuyên bạn nên sử dụng boost::thread.

Trường hợp khởi động cơ bản / công nghệ

Nếu bạn đang viết cho một hoặc hai hệ điều hành, bạn chắc chắn rằng bạn sẽ chỉ cần xây dựng với trình biên dịch hiện đại chủ yếu hỗ trợ C ++ 11 (ví dụ: VS2015, GCC 5.3, Xcode 7), và bạn chưa phụ thuộc vào thư viện boost, sau đó std::threadcó thể là một lựa chọn tốt.

Kinh nghiệm của tôi

Cá nhân tôi là một phần của các thư viện cứng, được sử dụng nhiều, tương thích cao, rất nhất quán như boost so với một giải pháp thay thế rất hiện đại. Điều này đặc biệt đúng đối với các môn lập trình phức tạp như luồng. Ngoài ra, từ lâu tôi đã trải nghiệm thành công lớn với boost::thread(và boost nói chung) trên một loạt các môi trường, trình biên dịch, mô hình luồng, v.v. Khi tôi chọn, tôi chọn boost.


1
@UmNyobe Anh nói đúng. Nhiều triển khai của luồng C ++ 11 đã bị hỏng, tôi ngạc nhiên khi mọi người cân nhắc sử dụng nó.
StaceyGirl

3

Với Visual Studio 2013 std::mutexdường như hoạt động khác với boost::mutex, điều này gây ra cho tôi một số vấn đề (xem câu hỏi này ).


1

Liên quan đến std :: shared_mutex được thêm vào trong C ++ 17

Các câu trả lời khác ở đây cung cấp một cái nhìn tổng quan rất tốt về sự khác biệt nói chung. Tuy nhiên, có một số vấn đề với std::shared_mutexgiải quyết tăng đó.

  1. Đột biến nâng cấp. Đây là vắng mặt std::thread. Chúng cho phép người đọc được nâng cấp thành nhà văn mà không cho phép bất kỳ nhà văn nào khác vào trước bạn . Điều này cho phép bạn thực hiện những việc như xử lý trước một tính toán lớn (ví dụ: lập lại cấu trúc dữ liệu) khi ở chế độ đọc, sau đó nâng cấp lên ghi để áp dụng reindex trong khi chỉ giữ khóa ghi trong một thời gian ngắn.

  2. Công bằng. Nếu bạn có hoạt động đọc liên tục với a std::shared_mutex, các nhà văn của bạn sẽ bị khóa mềm vô thời hạn. Điều này là bởi vì nếu một người đọc khác xuất hiện, họ sẽ luôn được ưu tiên. Với boost:shared_mutex, tất cả các chủ đề cuối cùng sẽ được ưu tiên. (1) Cả độc giả lẫn nhà văn sẽ không bị bỏ đói.

Vấn đề của điều này là nếu bạn có một hệ thống thông lượng rất cao, không có thời gian chết và sự tranh chấp rất cao, std::shared_mutexsẽ không bao giờ có hiệu quả đối với bạn mà không xây dựng một hệ thống ưu tiên trên nó. boost::shared_mutexsẽ hoạt động tốt, mặc dù bạn có thể cần phải sửa lại nó trong một số trường hợp nhất định. Tôi cho rằng std::shared_mutexhành vi của nó là một lỗi tiềm ẩn đang chờ xảy ra trong hầu hết các mã sử dụng nó.

(1) Các thuật toán thực tế nó sử dụng được dựa trên lịch trình hệ điều hành chủ đề. Theo kinh nghiệm của tôi, khi các lần đọc đã bão hòa, sẽ có các lần tạm dừng dài hơn (khi có khóa ghi) trên Windows so với OSX / Linux.


0

Tôi đã cố gắng sử dụng shared_ptr từ std thay vì boost và tôi thực sự tìm thấy một lỗi trong việc triển khai gcc của lớp này. Ứng dụng của tôi đã bị sập vì hàm hủy được gọi hai lần (lớp này phải an toàn cho luồng và không nên tạo ra các vấn đề như vậy). Sau khi di chuyển đến boost :: shared_ptr, mọi vấn đề đều biến mất. Việc triển khai C ++ 11 hiện tại vẫn chưa trưởng thành.

Boost cũng có nhiều tính năng hơn. Ví dụ: tiêu đề trong phiên bản std không cung cấp serializer cho luồng (tức là cout << thời gian). Boost có nhiều thư viện sử dụng tương đương, v.v., nhưng không hợp tác với các phiên bản tiêu chuẩn.

Tóm lại - nếu bạn đã có một ứng dụng được viết bằng boost, sẽ an toàn hơn khi giữ mã của bạn vì nó thay vì bỏ một số nỗ lực để chuyển sang tiêu chuẩn C ++ 11.


4
Hàm shared_ptrhủy không cần phải an toàn cho luồng, đó là hành vi không xác định để có một luồng truy cập vào một đối tượng trong khi một luồng khác đang phá hủy nó. Nếu bạn nghĩ rằng bạn đã tìm thấy một lỗi trong shared_ptr của GCC, vui lòng báo cáo nó , nếu không thì trên số dư xác suất bạn sử dụng sai.
Jonathan Wakely
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.