2012-03-19 32 views
5

Tôi đang lập trình ứng dụng đa luồng. Tôi có hai chủ đề. Bật là để chuyển một số dữ liệu từ thiết bị sang bộ đệm dữ liệu toàn cục và thứ hai là ghi dữ liệu đó vào tệp.Cách thực hành tốt nhất về cách đợi dữ liệu trong chuỗi và ghi vào tệp?

Dữ liệu từ thiết bị đến bộ đệm đang chuyển không đồng bộ. Mục đích của luồng thứ hai nên đợi cho số lượng dữ liệu được chỉ định được ghi vào bộ đệm dữ liệu chính và cuối cùng là ghi nó vào tệp.

Chủ đề đầu tiên ở định dạng DLL và chuỗi thứ hai nằm trong ứng dụng chính. Tạm thời tôi giải quyết điều này với các sự kiện. Chủ đề đầu tiên chuyển dữ liệu từ thiết bị đến bộ đệm dữ liệu chính và đếm dữ liệu và khi số lượng dữ liệu được chỉ định được truyền, nó sẽ đặt một sự kiện. Thứ hai chờ đợi cho sự kiện được báo hiệu và khi nào nó chạy một số mã để lưu trữ dữ liệu. Đơn giản vì nó đang hoạt động.

Thread1.Execute: 

    var DataCount, TransferedData: Integer; 

    DataCounter := 0; 
    while not Terminted do 
    begin 
    TransferData(@pData, TransferedData); 
    Inc(DataCounter, TransferedData) 
    if DataCounter >= DataCountToNotify then SetEvent(hDataCount); 
    end; 



    Thread2.Execute: 

    hndlArr[0] := hDataCount; 
    hndlArr[1] := hTerminateEvent; 

    while (not Terminated) do 
    begin 
    wRes := WaitForMultipleObjects(HandlesCount, Addr(hndlArr), false, 60000); 
    case wRes of 
     WAIT_OBJECT_0: 
     begin 
      Synchronize(WriteBuffer);     // call it from main thread 
      ResetEvent(hndlArr[0]); 
     end; 
     WAIT_OBJECT_0 + 1: 
     begin 
      ResetEvent(hTerminateEvent); 
      break; 
     end; 
     WAIT_TIMEOUT: Break; 
    end; 
    end; 

Bây giờ tôi muốn tạo chuỗi thứ hai độc lập hơn ... vì vậy tôi có thể tạo nhiều phiên bản của chuỗi thứ hai và tôi không cần phải chờ chuỗi đầu tiên. Tôi muốn di chuyển dữ liệu đếm một phần mã từ chuỗi đầu tiên sang chuỗi thứ hai để tôi không cần đếm dữ liệu trong chuỗi đầu tiên nữa. Đầu tiên sẽ chỉ dành cho mục đích chuyển dữ liệu.

Tôi muốn sử dụng thứ hai làm bộ đếm dữ liệu và lưu trữ dữ liệu. Nhưng bây giờ tôi sẽ phải lặp lại và liên tục kiểm tra thủ công cho số lượng dữ liệu được chỉ định. Nếu tôi có vòng lặp, tôi sẽ phải thêm một số giấc ngủ để chủ đề thứ hai sẽ không làm giảm hiệu suất máy tính nhưng tôi không biết phải ngủ bao lâu trong khi tốc độ truyền dữ liệu trong chuỗi không ổn định và do đó tốc độ đếm trong chuỗi thứ hai sẽ thay đổi.

Tôi đoán rằng mẫu mã này là không tốt:

Thread2.Execute: 
    var DataCount: Integer; 
    DataIdx1 := GetCurrentDataIdx; 
    while (not Terminated) do 
    begin  
    if (GetCurrentDataIdx - DataIdx1) >= DataCountToNotify then 
    begin 
     Synchronize(WriteBuffer); 
     DataIdx1 := GetCurrentIdx; 
    end; 
    sleep(???); 
    end; 

Vì vậy, câu hỏi của tôi là phương pháp tốt nhất để giải quyết vấn đề đó với đếm dữ liệu và lưu trữ nó trong chủ đề thứ hai là gì? Kinh nghiệm và đề xuất của bạn là gì?

+3

Tại sao bạn buộc ghi tệp vào chuỗi chính, 'Đồng bộ hóa (WriteBuffer) '? Tôi không hiểu làm thế nào bạn sẽ quản lý để nhân các chủ đề thứ hai và vẫn duy trì để viết một tập tin duy nhất. –

+0

Chủ đề sẽ được nhân nhưng dữ liệu sẽ được lưu vào các tệp khác nhau. Tôi không có nơi nào viết, tôi sẽ viết nó vào cùng một tập tin. Mục tiêu của tôi là có nhiều điều khiển có thể lưu trữ dữ liệu từ cùng một nguồn đến các tệp khác nhau. – Nix

+0

Điều đó là tốt để biết, vì câu trả lời sẽ phụ thuộc vào thông tin này. Tôi vẫn tự hỏi tại sao việc ghi tập tin phải được thực thi với chủ đề chính. –

Trả lời

5

Bạn có một số vấn đề. @LU RD đã chỉ ra một điều - không đồng bộ hóa những thứ không cần phải được đồng bộ hóa. Nó không rõ ràng những gì 'WriteBuffer' hiện, nhưng hệ thống tập tin và tất cả các cơ sở dữ liệu tôi đã sử dụng chỉ là tốt để có một thread mở một tập tin/bảng và viết cho họ.

Hệ thống đệm của bạn có thể thực hiện với một số sự chú ý. Có một số 'số lượng dữ liệu được chỉ định' hay đây là một số con số không chính thức cho phép viết lách?

Thông thường, nhà sản xuất và chủ đề người tiêu dùng trao đổi nhiều bộ đệm trên hàng đợi và vì vậy tránh chia sẻ bất kỳ bộ đệm đơn nào. Cho rằng đây là một DLL và vì vậy các cuộc gọi quản lý bộ nhớ có thể có vấn đề, tôi có lẽ sẽ tránh chúng cũng bằng cách tạo ra một nhóm các bộ đệm lúc khởi động để chuyển dữ liệu vòng hệ thống. Tôi sẽ sử dụng một lớp đệm thay vì chỉ trỏ đến bộ nhớ, nhưng nó không hoàn toàn cần thiết, (dễ dàng hơn/linh hoạt/an toàn hơn).

Vòng lặp() là một cách giao tiếp giữa các luồng. Sleep() không sử dụng nó, nhưng đây không phải là một trong số chúng. Delphi/Windows có rất nhiều cơ chế đồng bộ hóa - sự kiện, semaphores, mutexes vv - tha làm cho việc bỏ phiếu như vậy không cần thiết.

LU RD cũng đã đề cập đến các vấn đề về xử lý song song dữ liệu có thứ tự phải được giữ nguyên. Điều này thường yêu cầu một chủ đề khác, một bộ sưu tập kiểu danh sách và số thứ tự. Tôi sẽ không thử điều đó cho đến khi bạn có các giao tiếp giữa các luồng hoạt động tốt.

+0

Mã trên chỉ dành cho mục đích demo. Đừng bận tâm với Synchronize. Tôi đã có những thứ cố định và tôi sử dụng cơ chế thích hợp cho các chủ đề synhronizing vv Như tôi đề cập đến tôi muốn đếm dữ liệu và sau đó khi điều kiện áp dụng cho ví dụ. DataCountToNotify = 8192 Tôi muốn lưu trữ dữ liệu. Nhưng tôi muốn tránh ngủ. Có cách nào khác để làm thế không? – Nix

+0

@Nix nếu tôi không nhầm, miễn là chuỗi của bạn có "cái gì đó" trong vòng lặp, sau đó bạn có thể bỏ qua "giấc ngủ", nhưng bạn có thể sử dụng giấc ngủ (1). 55 ms (win xp có vấn đề, không chắc chắn về các phiên bản sau) – ComputerSaysNo

+1

@DorinDuminica, xem [thread-sleep-is-a-sign-of-a-kém được thiết kế-chương trình] (https://msmvps.com/ blog/peterritchie/archive/2007/04/26/thread-sleep-là-một-đăng-một-kém-được thiết kế-program.aspx) cho các đối số chống lại bằng cách sử dụng 'Sleep' trong một chủ đề. –

2

Nếu bạn muốn tránh cuộc gọi Sleep() trong chuỗi thứ hai của mình, hãy sử dụng bộ hẹn giờ có thể chờ đợi như TSimpleEvent.

Đặt thời gian ngủ để xử lý tất cả các điều kiện thời gian của bạn. Có một lợi thế của việc sử dụng lược đồ này thay vì một bình thường Sleep(), vì bộ đếm thời gian chờ đợi sẽ không đưa chủ đề vào giấc ngủ sâu.

Để vứt bỏ chuỗi, hãy xem nhận xét bằng mã.

var 
    FEvent: TSimpleEvent; 
    FSleepTime: Integer = 100; // Short enough to handle all cases 

Constructor TThread2.Create; 
begin 
    Inherited Create(False); 
    FEvent := TSimpleEvent.Create; 
    Self.FreeOnTerminate := True; 
end; 

procedure TThread2.Execute; 
var 
    DataCount: Integer; 
begin 
    DataIdx1 := GetCurrentDataIdx; 
    while (fEvent.WaitFor(FSleepTime) = wrTimeout) do 
    begin 
    if Terminated then 
     break; 
    // Do your work 
    if (GetCurrentDataIdx - DataIdx1) >= DataCountToNotify then 
    begin 
     // Write data to buffer 
     DataIdx1 := GetCurrentIdx; 
    end; 
    end; 
end; 

// To stop the thread gracefully, call this instead of Terminate or override the DoTerminate 
procedure TThread2.SetTerminateFlag; 
begin 
    FEvent.SetEvent; 
end; 
+0

LU RD cảm ơn điều này có vẻ rất hứa hẹn. Tôi sẽ phải thay đổi thời gian FSleep tùy thuộc vào tốc độ truyền dữ liệu trong chuỗi đầu tiên. Tôi có thể tính toán thời gian đó. Giống như tôi đã nói tôi cố gắng tránh ngủ trong sợi chỉ vì vậy đó là lý do tại sao tôi đang tìm kiếm một lựa chọn tốt hơn để ngủ. Tôi đã không bao giờ sử dụng giấc ngủ trong bất kỳ chủ đề tôi lập trình. Tôi không thấy nó rất an toàn. Tôi thích hệ thống được quyết định hơn. Cảm ơn rất nhiều. Tôi sẽ cố gắng nhận ra nó theo cách bạn gợi ý. – Nix

+0

Điều này là hợp lý trên cái nhìn đầu tiên, nhưng nó không phải. Ý của bạn là 'wrSignaled' thay vì 'wrTimeout'? Ngoài ra, nó vẫn còn có các cuộc gọi Sycnhronize ở đây mà làm cho các chủ đề tồi tệ hơn không hiệu quả - nó sẽ thực sự giới thiệu chậm trễ hơn. –

+0

@MartinJames, Đồng bộ hóa rõ ràng là sai, nhưng OP cho biết đây không còn là vấn đề nữa. Vòng lặp sẽ tiếp tục chạy trong khi không được báo hiệu (mỗi mili giây của FSleepTime). Điều này có vẻ ok trong tâm trí của tôi. –

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