Làm thế nào tôi có thể làm cho thông điệp chuyển giữa các luồng trong một công cụ đa luồng ít cồng kềnh hơn?


18

Công cụ C ++ mà tôi đang làm việc hiện đang được chia thành nhiều luồng lớn - Tạo (để tạo nội dung thủ tục của tôi), Trò chơi (cho AI, kịch bản, mô phỏng), Vật lý và Kết xuất.

Các luồng giao tiếp với nhau thông qua các đối tượng tin nhắn nhỏ, truyền từ luồng này sang luồng khác. Trước khi bước, một luồng xử lý tất cả các thông báo đến của nó - cập nhật để biến đổi, thêm và xóa các đối tượng, v.v. Đôi khi, một luồng (Tạo) sẽ tạo ra một thứ gì đó (Nghệ thuật) và chuyển nó sang một luồng khác (Kết xuất) để sở hữu vĩnh viễn.

Đầu quá trình và tôi đã nhận thấy một vài điều:

  1. Hệ thống nhắn tin cồng kềnh. Tạo một loại thông báo mới có nghĩa là phân lớp lớp Thông báo cơ sở, tạo một enum mới cho loại của nó và viết logic cho cách các chủ đề sẽ diễn giải loại tin nhắn mới. Đó là một cú hích tốc độ để phát triển và dễ bị lỗi kiểu chữ. (Sidenote- làm việc về điều này khiến tôi đánh giá cao những ngôn ngữ năng động tuyệt vời như thế nào!)

    Có cách nào tốt hơn để làm điều này? Tôi có nên sử dụng một cái gì đó như boost :: bind để thực hiện điều này tự động không? Tôi lo lắng rằng nếu tôi làm điều đó tôi sẽ mất khả năng nói, sắp xếp các tin nhắn dựa trên loại hoặc một cái gì đó. Không chắc chắn nếu loại quản lý đó thậm chí sẽ trở nên cần thiết.

  2. Điểm đầu tiên rất quan trọng vì các chủ đề này giao tiếp rất nhiều. Tạo và gửi tin nhắn là một phần lớn trong việc làm cho mọi thứ xảy ra. Tôi muốn hợp lý hóa hệ thống đó, nhưng cũng mở ra cho các mô hình khác có thể hữu ích như vậy. Có những thiết kế đa luồng khác nhau mà tôi nên nghĩ đến để giúp làm điều này dễ dàng hơn không?

    Ví dụ, có một số tài nguyên được viết không thường xuyên, nhưng thường xuyên được đọc từ nhiều luồng. Tôi có nên cởi mở với ý tưởng có dữ liệu chia sẻ, được bảo vệ bởi các mutexes, rằng tất cả các luồng có thể truy cập không?

Đây là lần đầu tiên tôi thiết kế một cái gì đó với ý tưởng đa luồng từ đầu. Ở giai đoạn đầu này, tôi thực sự nghĩ rằng nó sẽ thực sự tốt (xem xét) nhưng tôi lo lắng về việc mở rộng quy mô và hiệu quả của chính tôi trong việc thực hiện các công cụ mới.


6
Thực sự không có một câu hỏi trực tiếp nào, và vì vậy bài đăng này không phù hợp với phong cách hỏi đáp của trang web này. Tôi khuyên bạn nên chia bài viết của mình thành các bài đăng riêng biệt, mỗi câu hỏi và tập trung lại các câu hỏi để họ hỏi về một vấn đề cụ thể mà bạn thực sự gặp phải thay vì một bộ sưu tập lời khuyên hoặc lời khuyên mơ hồ.

2
Nếu bạn muốn tham gia vào một cuộc trò chuyện tổng quát hơn, tôi khuyên bạn nên thử bài đăng này trên các diễn đàn tại gamedev.net . Như Josh đã nói, vì "câu hỏi" của bạn không phải là một câu hỏi cụ thể, nên sẽ rất khó để viết theo định dạng StackExchange.
Cypher

Cảm ơn các bạn đã phản hồi! Tôi đã hy vọng ai đó có nhiều kiến ​​thức hơn có thể có một tài nguyên / kinh nghiệm / mô hình duy nhất có thể giải quyết một số vấn đề của tôi cùng một lúc. Tôi có cảm giác rằng một ý tưởng lớn có thể tổng hợp các vấn đề khác nhau của tôi thành một điều mà tôi đang thiếu, và tôi đang nghĩ ai đó có nhiều kinh nghiệm hơn tôi có thể nhận ra rằng ... Nhưng có lẽ không, và dù sao đi nữa !
Raptormeat

Tôi đã đổi tên tiêu đề của bạn thành cụ thể hơn để chuyển tin nhắn, vì các câu hỏi loại "mẹo" ngụ ý rằng không có vấn đề cụ thể nào để giải quyết (và do đó những ngày này tôi đóng là "không phải là một câu hỏi thực sự").
Tetrad

Bạn có chắc chắn bạn cần các chủ đề riêng biệt cho vật lý và chơi game? Hai người đó dường như rất hòa quyện vào nhau. Ngoài ra, thật khó để biết cách đưa ra lời khuyên mà không biết mỗi người trong số họ giao tiếp với ai.
Nicol Bolas

Câu trả lời:


10

Đối với vấn đề rộng hơn của bạn, hãy cân nhắc việc cố gắng tìm cách giảm bớt giao tiếp giữa các luồng càng nhiều càng tốt. Tốt hơn hết là tránh các vấn đề đồng bộ hóa, nếu bạn có thể. Điều này có thể đạt được bằng cách nhân đôi dữ liệu của bạn, giới thiệu độ trễ cập nhật một lần nhưng giảm đáng kể nhiệm vụ làm việc với dữ liệu được chia sẻ.

Như một bên, bạn đã xem xét không phân luồng bởi hệ thống con, mà thay vào đó sử dụng sinh sản luồng, hoặc nhóm luồng để rẽ nhánh theo nhiệm vụ? (xem phần này liên quan đến vấn đề cụ thể của bạn, để tổng hợp chủ đề.) Bài viết ngắn này phác thảo chính xác mục đích và cách sử dụng của mẫu hồ bơi. Xem những câu trả lời thông tincũng thế. Như đã lưu ý ở đó, nhóm chủ đề cải thiện khả năng mở rộng như một phần thưởng. Và đó là "viết một lần, sử dụng bất cứ nơi nào", trái ngược với việc phải có các chủ đề dựa trên hệ thống con để chơi tốt mỗi khi bạn viết một trò chơi hoặc công cụ mới. Ngoài ra còn có rất nhiều giải pháp tổng hợp chủ đề của bên thứ ba. Sẽ đơn giản hơn để bắt đầu với việc sinh sản luồng và làm việc theo cách của bạn để nhóm luồng sau, nếu chi phí sinh sản và phá hủy các luồng cần phải được giảm thiểu.


1
Bất kỳ đề xuất cho libs chủ đề cụ thể để kiểm tra?
imre

Nick- cảm ơn rất nhiều vì đã phản hồi. Theo quan điểm đầu tiên của bạn - Tôi nghĩ đó là một ý tưởng tuyệt vời và có lẽ là hướng tôi sẽ đi vào. Hiện tại, tôi còn sớm để biết mình cần gì để được đệm đôi. Tôi sẽ ghi nhớ điều này vì nó sẽ ổn định theo thời gian. Đến điểm thứ hai của bạn - cảm ơn vì lời đề nghị! Vâng, lợi ích của các nhiệm vụ chủ đề là rõ ràng. Tôi sẽ đọc các liên kết của bạn và suy nghĩ về nó. Không chắc chắn 100% nếu nó sẽ làm việc cho tôi / làm thế nào để nó hoạt động với tôi nhưng tôi chắc chắn sẽ suy nghĩ nghiêm túc. Cảm ơn!
Raptormeat

1
@imre hãy kiểm tra thư viện Boost - họ có tương lai, đây là một cách dễ dàng / dễ dàng để tiếp cận những điều này.
Jonathan Dickinson

5

Bạn hỏi về các thiết kế đa luồng khác nhau. Một người bạn của tôi nói với tôi về phương pháp này mà tôi nghĩ là khá tuyệt.

Ý tưởng là sẽ có 2 bản sao của mỗi thực thể trò chơi (lãng phí, tôi biết). Một bản sao sẽ là bản sao hiện tại và bản còn lại sẽ là bản sao trong quá khứ. Bản sao hiện tại chỉ được viết một cách nghiêm ngặt và bản sao quá khứ chỉ được đọc một cách nghiêm ngặt . Khi bạn đi cập nhật, bạn chỉ định phạm vi của danh sách thực thể của mình cho nhiều chủ đề mà bạn thấy phù hợp. Mỗi luồng có quyền truy cập ghi vào các bản sao hiện tại trong phạm vi được chỉ định và mọi luồng có quyền truy cập đọc vào tất cả các bản sao trước đây của các thực thể và do đó có thể cập nhật các bản sao hiện tại được chỉ định bằng cách sử dụng dữ liệu từ các bản sao trước đó mà không bị khóa. Giữa mỗi khung, bản sao hiện tại trở thành bản sao trong quá khứ, tuy nhiên bạn muốn xử lý việc hoán đổi vai trò.


4

Chúng tôi đã có cùng một vấn đề, chỉ với C #. Sau khi suy nghĩ lâu và khó về việc dễ dàng (hoặc thiếu nó) trong việc tạo tin nhắn mới, điều tốt nhất chúng ta có thể làm là tạo một trình tạo mã cho chúng. Nó hơi xấu, nhưng có thể sử dụng được: chỉ đưa ra một mô tả về nội dung tin nhắn, nó tạo ra lớp tin nhắn, enums, mã xử lý giữ chỗ, v.v. - tất cả các mã này gần như giống nhau mỗi lần và thực sự dễ bị lỗi chính tả.

Tôi không hoàn toàn hài lòng với điều này, nhưng tốt hơn là viết tất cả mã đó bằng tay.

Liên quan đến dữ liệu được chia sẻ, câu trả lời tốt nhất dĩ nhiên là "nó phụ thuộc". Nhưng nói chung, nếu một số dữ liệu được đọc thường xuyên và cần nhiều chủ đề, thì việc chia sẻ nó là đáng giá. Để đảm bảo an toàn cho luồng, đặt cược tốt nhất của bạn là làm cho nó không thay đổi , nhưng nếu không có vấn đề gì, mutex có thể làm được. Trong C # có một ReaderWriterLockSlimlớp, được thiết kế riêng cho các trường hợp như vậy; Tôi chắc chắn có một C ++ tương đương.

Một ý tưởng khác cho giao tiếp luồng, có lẽ giải quyết vấn đề đầu tiên của bạn, là chuyển các trình xử lý thay vì tin nhắn. Tôi không chắc chắn làm thế nào để giải quyết vấn đề này trong C ++, nhưng trong C #, bạn có thể gửi một delegateđối tượng đến một luồng khác (như trong, thêm nó vào một loại hàng đợi tin nhắn nào đó) và thực sự gọi đại biểu này từ luồng nhận. Điều này cho phép tạo tin nhắn "ad hoc" ngay lập tức. Tôi chỉ đùa với ý tưởng này, chưa bao giờ thực sự thử nó trong sản xuất, vì vậy thực tế nó có thể trở nên tồi tệ.


Cảm ơn tất cả các thông tin tuyệt vời! Phần cuối cùng về trình xử lý tương tự như những gì tôi đã đề cập về việc sử dụng ràng buộc hoặc hàm xử lý để truyền các hàm. Tôi giống như ý tưởng - Tôi có thể dùng thử và xem nó có hấp dẫn hay tuyệt vời không: D Có thể bắt đầu chỉ bằng cách tạo một lớp CallDelegateMessage và nhúng ngón chân xuống nước.
Raptormeat

1

Tôi chỉ đang trong giai đoạn thiết kế một số mã trò chơi theo chuỗi, vì vậy tôi chỉ có thể chia sẻ suy nghĩ của mình chứ không phải bất kỳ trải nghiệm thực tế nào. Như đã nói, tôi đang suy nghĩ theo các dòng sau:

  • Hầu hết dữ liệu trò chơi nên được chia sẻ để truy cập chỉ đọc .
  • Viết dữ liệu có thể sử dụng một loại tin nhắn.
  • Để tránh cập nhật dữ liệu trong khi một luồng khác đang đọc nó, vòng lặp trò chơi có hai giai đoạn riêng biệt: đọc và cập nhật.
  • Trong giai đoạn đọc:
  • Tất cả dữ liệu được chia sẻ là chỉ đọc cho tất cả các chủ đề.
  • Chủ đề có thể tính toán nội dung (sử dụng lưu trữ luồng cục bộ) và tạo yêu cầu cập nhật , về cơ bản là các đối tượng lệnh / thông báo, được đặt trong hàng đợi, sẽ được áp dụng sau.
  • Trong giai đoạn cập nhật:
  • Tất cả dữ liệu được chia sẻ là chỉ ghi. Dữ liệu được giả định ở trạng thái không xác định / không ổn định.
  • Đây là nơi các đối tượng yêu cầu cập nhật được xử lý.

Tôi nghĩ (mặc dù tôi không chắc chắn) về mặt lý thuyết, điều này có nghĩa là trong cả hai giai đoạn đọc và cập nhật, bất kỳ số lượng luồng nào cũng có thể chạy đồng thời với việc đồng bộ hóa tối thiểu. Trong giai đoạn đọc, không ai viết dữ liệu chia sẻ, do đó không có vấn đề tương tranh nào phát sinh. Giai đoạn cập nhật, tốt, đó là khó khăn hơn. Cập nhật song song trên cùng phần dữ liệu sẽ là một vấn đề, vì vậy một số đồng bộ hóa theo thứ tự ở đây. Tuy nhiên, tôi vẫn có thể chạy một số luồng cập nhật tùy ý, miễn là chúng hoạt động trên các tập dữ liệu khác nhau.

Nói chung, tôi nghĩ cách tiếp cận này sẽ cho vay chính nó cho một hệ thống tổng hợp luồng. Các phần có vấn đề là:

  • Đồng bộ hóa các luồng cập nhật (đảm bảo không có nhiều luồng cố gắng cập nhật cùng một tập dữ liệu).
  • Đảm bảo rằng trong giai đoạn đọc, không có luồng nào có thể vô tình ghi dữ liệu chia sẻ. Tôi e rằng sẽ có quá nhiều chỗ cho các lỗi lập trình và tôi không chắc có bao nhiêu trong số chúng có thể dễ dàng bị các công cụ gỡ lỗi bắt gặp.
  • Viết mã theo cách mà bạn không thể dựa vào kết quả trung gian có sẵn để đọc ngay lập tức. Đó là, bạn không thể viết x += 2; if (x > 5) ...nếu x được chia sẻ. Bạn cần tạo một bản sao cục bộ của x hoặc tạo một yêu cầu cập nhật và chỉ thực hiện điều kiện trong lần chạy tiếp theo. Cái sau có nghĩa là toàn bộ rất nhiều mã bổ sung trạng thái cục bộ bảo quản.
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.