2016-07-01 28 views
16

Trong đợt kiểm tra mã, tôi đi qua một đoạn mã mà về cơ bản nắm này:gì sẽ xảy ra khi giao lại cho một tương lai mà không phải là sẵn sàng chưa

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

int main(int, char **) 
{ 
    std::atomic<int> x(0); 
    std::future<void> task; 
    for(std::size_t i = 0u; i < 5u; ++i) 
    { 
     task = std::async(std::launch::async, [&x, i](){ 
       std::this_thread::sleep_for(std::chrono::seconds(2u * (5u - i))); 
       ++x; 
      }); 
    } 

    task.get(); 
    std::cout << x << std::endl; 
    return 0; 
} 

Tôi đã không hoàn toàn chắc chắn cho dù

  • đảm bảo rằng tất cả các tác vụ được thực hiện khi in kết quả,
  • cho dù nhiệm vụ sẽ được thực hiện cái khác (nghĩa là nhiệm vụ sẽ bị chặn) hay không.

Tôi không thể trả lời câu hỏi đó từ đọc tài liệu trên internet, vì vậy tôi nghĩ tôi sẽ viết đoạn mã trên để tìm hiểu trình biên dịch của chúng tôi thực sự làm gì.

Bây giờ, tôi phát hiện ra rằng câu trả lời của gcc-5 là thiếu quyết đoán và điều đó khiến tôi tò mò hơn: Người ta cho rằng nhiệm vụ đó là chặn hoặc không chặn.

Nếu bị chặn, thời gian thực hiện bởi chương trình về cơ bản là tổng thời gian thực hiện các tác vụ riêng lẻ. Người đầu tiên mất 10 giây, thứ hai 8, thứ ba 6, thứ tư 4 và 2 giây cuối cùng. Do đó, tổng cộng sẽ mất 10 + 8 + 6 + 4 + 2 = 30 giây.

Nếu nó không bị chặn, bạn nên thực hiện nhiệm vụ cuối cùng, tức là 2 giây.

Đây là những gì sẽ xảy ra: Phải mất 18 giây (được đo bằng thời gian ./a.out hoặc đồng hồ cũ tốt). Bằng cách chơi xung quanh một chút với mã tôi phát hiện ra rằng mã hoạt động như thể nhiệm vụ sẽ được xen kẽ chặn và không chặn.

Nhưng điều này không thể đúng, phải không? std::async có thể rơi trở lại std::deferred một nửa thời gian? Trình gỡ rối của tôi nói rằng nó sinh ra hai luồng, khối cho đến khi cả hai luồng thoát ra, sau đó sinh ra hai luồng nữa và tiếp tục như vậy.

Tiêu chuẩn nói gì? Chuyện gì sẽ xảy ra? Điều gì xảy ra bên trong gcc-5?

+1

Thực tế là mã không mất khoảng thời gian dự kiến ​​hoàn thành là lạ, nhưng điều ** thực sự lạ đối với tôi là giá trị của 'x' được gán đúng 5, trong khi' ngủ ' rõ ràng là không hoàn thành ... – Holt

+0

Bạn thực sự nên in thời gian trôi qua sau khi các cuộc gọi 'sleep' trong lambda của bạn bởi vì bạn sẽ rất ngạc nhiên tôi nghĩ! – Holt

+0

@Holt Việc thực thi 'gcc' đang chặn * đôi khi *, tất cả đều hoạt động trong trường hợp này, nhưng có thể xảy ra sai trong các trường hợp khác. Tôi đã xem nó trong trình gỡ lỗi: 0 bắt đầu, 1 bắt đầu, 1 lần thoát, 0 lần thoát, 2 lần khởi động, 3 lần khởi động, 3 lần thoát, 2 lần thoát, 4 lần khởi động, 4 lần thoát. –

Trả lời

9

Việc chuyển nhượng task qua operator=(&&) không buộc phải được ngăn chặn (xem dưới đây), nhưng trong trường hợp của bạn, bạn đã tạo ra std::future sử dụng std::async vì vậy nó được chặn (nhờ @TC):

[future.async]

Nếu việc thực hiện lựa chọn chính sách ra mắt :: async,

  • [...]

  • hoàn thành chuỗi liên kết đồng bộ hóa với ([intro.multithread]) trả về từ hàm đầu tiên phát hiện thành công trạng thái sẵn sàng của trạng thái được chia sẻ hoặc với lợi nhuận từ hàm cuối cùng phát hành trạng thái chia sẻ, tùy điều kiện nào xảy ra trước.

Tại sao bạn nhận được thời gian thực hiện 18 giây?

gì xảy ra trong trường hợp của bạn là std::async bắt đầu "chủ đề" cho lambda của bạn trước sự phân công - Xem dưới đây để giải thích chi tiết về làm thế nào bạn nhận được một thời gian thực hiện 18 giây.

Đây là những gì (có lẽ) sẽ xảy ra trong mã của bạn (e đứng cho một epsilon):

  • t = 0, std::async cuộc gọi đầu tiên với i = 0, bắt đầu một chủ đề mới;
  • t = 0 + e, thứ hai std::async cuộc gọi với i = 1 bắt đầu một chủ đề mới, sau đó di chuyển: Động thái này sẽ phát hành dòng chia sẻ trạng thái củatask, ngăn chặn khoảng 10 giây (nhưng thứ hai std::async với i = 1 đã được thực hiện);
  • t = 10, thứ ba std::async cuộc gọi với i = 2 bắt đầu một chủ đề mới, sau đó di chuyển: Các hiện chia sẻ trạng thái củatask là cuộc gọi với i = 1 mà đã sẵn sàng nên không có gì ngăn chặn;
  • t = 10 + e, thứ tư std::async cuộc gọi với i = 3 bắt đầu một chủ đề mới, sau đó di chuyển: Động thái này được chặn bởi vì trước std::async với i = 2 chưa sẵn sàng nhưng các chủ đề cho i = 3 như đã bắt đầu;
  • t = 16, thứ năm std::async cuộc gọi với i = 4 bắt đầu một chủ đề mới, sau đó di chuyển: Trạng thái hiện tại chia sẻ của task (i = 3) đã sẵn sàng, vì vậy non-blocking;
  • t = 16 + e, ngoài vòng lặp, hãy gọi tới số .get() để chờ * trạng thái chia sẻ` sẵn sàng;
  • t = 18, trạng thái chia sẻ trở nên sẵn sàng để toàn bộ nội dung kết thúc.

chi tiết tiêu chuẩn trên std::future::operator=:

Dưới đây là trích dẫn tiêu chuẩn cho operator= trên std::future:

future& operator=(future&& rhs) noexcept; 

Effects:

  • (10,1) - phát hành bất kỳ chỉ số được chia sẻ e (30.6.4).
  • ...

Và đây là những gì "chí bất cứ tiểu bang được chia sẻ" trung bình (nhấn mạnh là của tôi):

Khi một đối tượng trở lại không đồng bộ hoặc một nhà cung cấp không đồng bộ được cho là để giải phóng nhà nước chia sẻ của mình, nó có nghĩa là:

(5,1) - [...]

(5,2) - [...]

01.235.

(5.3) - những hành động này sẽ không chặn trạng thái chia sẻ sẵn sàng, ngoại trừ việc có thể chặn nếu tất cả các điều sau là đúng: trạng thái chia sẻ được tạo bằng lệnh gọi tới std :: async, trạng thái chia sẻ chưa phải là đã sẵn sàng và đây là tham chiếu cuối cùng về trạng thái được chia sẻ.

trường hợp của bạn rơi vào những gì tôi đã nhấn mạnh (tôi nghĩ): bạn tạo ra tình trạng chia sẻ sử dụng std::async, nó đang ngủ (vì vậy không sẵn sàng) và bạn chỉ có một tham chiếu đến nó, vì vậy này có thể thể chặn .

+0

[đặc tả của 'async'] (http://eel.is/c++draft/futures.async#5) đảm bảo rằng nó sẽ chặn. ("hoàn thành chuỗi liên kết đồng bộ hóa với ([intro.multithread]) trả về từ hàm đầu tiên phát hiện thành công trạng thái sẵn sàng của trạng thái được chia sẻ hoặc trả về từ hàm cuối cùng phát hành trạng thái chia sẻ, tùy điều kiện nào xảy ra trước.") –

+0

@TC Cảm ơn, tôi đã khá chắc chắn rằng 'std :: future' được tạo với' std :: async' đã chặn (có thể đã thấy điều này trên SO một số ngày) nhưng tôi không thể tìm thấy trích dẫn chính xác trong tiêu chuẩn vì vậy tôi giả định sai rằng họ có thể chỉ đơn giản là chặn.Tôi đã cập nhật câu trả lời với phần có liên quan của tiêu chuẩn. – Holt

+0

Sử dụng VS2015 Tôi đã thiết lập mã và đề xuất của bạn về những gì xảy ra là chính xác ("trên trình biên dịch của tôi"). Tất nhiên, việc tạo 'task' là' tasks [5] 'cho phép tất cả các tác vụ chạy song song (cho phép đợi net trong 10 giây - nhiệm vụ dài nhất). –

3

nó được đảm bảo rằng tất cả các nhiệm vụ được thực hiện khi in ra kết quả,

Chỉ nhiệm vụ được giao trước được đảm bảo đã được thực hiện. Ít nhất, tôi không thể tìm thấy bất kỳ quy tắc nào có thể đảm bảo phần còn lại.

cho dù các tác vụ sẽ được thực thi cái khác (nghĩa là nhiệm vụ sẽ bị chặn) hay không.

Nhiệm vụ thường không bị chặn nhưng có thể chặn trong trường hợp này - không đảm bảo.

[futures.unique_future]

tương lai & operator = (tương lai & & RHS) noexcept;

  1. Effects:

    phát hành bất kỳ nhà nước chia sẻ ([futures.state]).

[futures.state]

  1. Khi một đối tượng trở lại không đồng bộ hoặc một nhà cung cấp không đồng bộ được cho là để giải phóng nhà nước chia sẻ của nó, nó có nghĩa là:

    • nếu đối tượng trả lại hoặc nhà cung cấp giữ tham chiếu cuối cùng về trạng thái được chia sẻ của nó, trạng thái chia sẻ bị hủy; và

    • đối tượng trả lại hoặc nhà cung cấp từ bỏ tham chiếu đến trạng thái được chia sẻ của nó; và

    • các hành động này sẽ không chặn trạng thái chia sẻ sẵn sàng, ngoại trừ nó có thể chặn nếu tất cả các điều sau là đúng: trạng thái chia sẻ được tạo bằng lời gọi đến std :: async, shared trạng thái chưa sẵn sàng và đây là tham chiếu cuối cùng cho trạng thái được chia sẻ.

Tất cả các điều kiện để ngăn chặn tiềm năng thực sự của nhiệm vụ tạo ra bởi std::async đã không thực hiện được nêu ra.

+0

Ah, * có thể chặn *, không đọc kỹ điều này, vì vậy về cơ bản, tiêu chuẩn không đảm bảo rằng nó đang chặn và cũng không bị chặn, phải không? –

+0

@MarkusMayr ​​trong trường hợp này, nó sẽ xuất hiện chính xác như vậy. Tôi cho rằng điều này có nghĩa là nó rơi vào thể loại hành vi được xác định thực hiện. – user2079303

Các vấn đề liên quan