2010-03-17 29 views
6

Tôi đang cố viết mã đa luồng và đối mặt với một số câu hỏi đồng bộ hóa. Tôi biết có rất nhiều bài viết ở đây nhưng tôi không thể tìm thấy bất cứ điều gì phù hợp.đồng bộ hóa chủ đề - không có giao diện người dùng

Tôi có một System.Timers.Timer đã trôi qua sau mỗi 30 giây, nó chuyển đến db và kiểm tra xem có bất kỳ công việc mới nào không. Nếu anh ta tìm thấy một, anh ta thực hiện công việc trên thread hiện tại (bộ đếm thời gian mở thread mới cho mỗi lần trôi qua). Trong khi công việc đang chạy, tôi cần phải thông báo cho chủ đề chính (nơi bộ đếm thời gian) về tiến trình.

Ghi chú:

  1. Tôi không có giao diện người dùng vì vậy tôi không thể làm beginInvoke (hoặc sử dụng sợi nền) như tôi thường làm trong winforms.
  2. Tôi nghĩ để triển khai ISynchronizeInvoke trên lớp học chính của mình nhưng có vẻ hơi quá mức (có thể tôi đã sai ở đây).
  3. Tôi có một sự kiện trong lớp công việc và đăng ký lớp học chính và tôi gọi sự kiện bất cứ khi nào tôi cần nhưng tôi lo rằng nó có thể gây ra sự chặn.
  4. Mỗi công việc có thể mất tối đa 20 phút.
  5. Tôi có thể có tối đa 20 công việc chạy đồng thời.

Câu hỏi của tôi là:

đúng cách để thông báo cho chủ đề chính của tôi về bất kỳ sự tiến bộ trong chủ đề công việc của tôi là gì?

Cảm ơn bạn đã được trợ giúp.

+0

Bạn không thể thông báo cho a thread. Bạn có thể thông báo cho một đối tượng. Mô tả một phần tốt hơn và bạn có thể nhận được câu trả lời tốt hơn. –

Trả lời

1

Sử dụng sự kiện. Ví dụ, lớp BackgroundWorker được thiết kế đặc biệt cho những gì bạn có trong tâm trí.

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

Các ReportProgress chức năng cùng với sự kiện ProgressChanged là những gì bạn sẽ sử dụng để cập nhật tiến độ.

pullJobTimer.Elapsed += (sender,e) => 
{ 
    BackgroundWorker worker = new BackgroundWorker(); 
    worker.WorkerReportsProgress = true; 
    worker.DoWork += (s,e) => 
    { 
     // Whatever tasks you want to do 
     // worker.ReportProgress(percentComplete); 
    }; 
    worker.ProgressChanged += mainThread.ProgressChangedEventHandler; 
    worker.RunWorkerAsync(); 
}; 
+0

Tôi cảm thấy sai khi sử dụng BackgroundWorker và tôi có thể giải thích lý do. System.Timers.Timer pullJobTimer = new System.Timers.Timer (INTERVAL); pullJobTimer.Elapsed + = new System.Timers.ElapsedEventHandler (PullQJobs); Tôi có thể mở một BackgroundWorker mới trong pulljobs đó là sự thật và sau đó tôi sẽ có ProgressChanged. Nhưng bộ đếm thời gian trôi qua sẽ mở ra một chuỗi mới và sau đó tôi sẽ mở một chủ đề nền mới ??? cảm thấy sai không? Hoặc có lẽ tôi đã không nhận được ý tưởng của bạn. – UshaP

+0

Bạn thậm chí không phải mở một chuỗi mới. BackgroundWorker làm điều đó cho bạn khi bạn chạy nó không đồng bộ thông qua 'RunWorkerAsync()'. Điều này cũng không thể hiện rò rỉ bộ nhớ, như Jim Mischel đã đề xuất, vì đăng ký sự kiện bị hủy với phiên bản BackgroundWorker khi nó được thu thập rác. – Jake

+0

Tôi biết rằng người làm việc nền sẽ mở một chuỗi nhưng cũng là bộ hẹn giờ. Vì vậy, sử dụng lớp chính Bộ hẹn giờ -> Bộ hẹn giờ sẽ mở một chuỗi mới cho mỗi lần trôi qua -> sau đó tôi chạy backgroundworker mở một chuỗi khác. Và đó là điều khiến tôi cảm thấy không thoải mái. – UshaP

1

Bạn có thể thông báo cho luồng chính của tiến trình thông qua phương thức gọi lại. Đó là:

// in the main thread 
public void ProgressCallback(int jobNumber, int status) 
{ 
    // handle notification 
} 

Bạn có thể vượt qua rằng phương pháp gọi lại để các sợi nhân khi bạn gọi nó (ví dụ như một đại biểu), hoặc mã các sợi nhân có thể "biết" về nó ngầm. Dù bằng cách nào cũng hiệu quả.

Công việcNumber và thông số trạng thái chỉ là ví dụ. Bạn có thể muốn bạn sử dụng một số cách khác để xác định các công việc đang chạy, và bạn có thể muốn sử dụng một kiểu liệt kê cho trạng thái. Tuy nhiên, bạn hãy nhớ rằng ProgressCallback sẽ được gọi bởi nhiều luồng đồng thời, vì vậy nếu bạn cập nhật bất kỳ cấu trúc dữ liệu được chia sẻ hoặc ghi thông tin ghi nhật ký, bạn sẽ phải bảo vệ các tài nguyên đó bằng khóa hoặc các kỹ thuật đồng bộ khác.

Bạn cũng có thể sử dụng các sự kiện cho việc này, nhưng việc cập nhật đăng ký sự kiện của chủ đề chính có thể là một vấn đề tiềm ẩn. Bạn cũng có tiềm năng bị rò rỉ bộ nhớ nếu bạn quên hủy đăng ký chuỗi chính từ các sự kiện của một chuỗi công việc cụ thể. Mặc dù các sự kiện chắc chắn sẽ hoạt động, tôi sẽ khuyên bạn nên gọi lại cho ứng dụng này.

+0

Jim, tôi nghĩ đây là cuộc gọi đồng bộ trong khi tôi cần cuộc gọi không đồng bộ – UshaP

+0

Gọi lại tiến trình được gọi trên chuỗi công nhân chứ không phải trên chuỗi chính. Vì vậy, nó là một cuộc gọi không đồng bộ từ quan điểm của chủ đề chính. –

1

Nếu bạn không nhớ tùy thuộc vào .NET 3.0, bạn có thể sử dụng Dispatcher cho các yêu cầu sắp xếp giữa các luồng.Nó hoạt động theo cách tương tự như Control.Invoke() trong Windows Forms nhưng không có phụ thuộc Biểu mẫu. Bạn sẽ cần phải thêm tham chiếu vào WindowsBase assembly (một phần của .NET 3.0 và mới hơn và là cơ sở cho WPF)

Nếu bạn không thể phụ thuộc vào .NET 3.0 thì tôi muốn nói bạn vào giải pháp chính xác ngay từ đầu: Triển khai giao diện ISynchronizeInvoke trong lớp chính của bạn và chuyển giao cho thuộc tính SynchronizingObject của Bộ hẹn giờ. Sau đó, gọi lại bộ đếm thời gian của bạn sẽ được gọi trên chủ đề chính, sau đó có thể sinh ra BackgroundWorkers để kiểm tra DB và chạy bất kỳ công việc xếp hàng nào. Các công việc sẽ báo cáo tiến độ thông qua sự kiện ProgressChanged sẽ tự động sắp xếp cuộc gọi đến chuỗi chính một cách tự động.

Tìm kiếm nhanh trên google tiết lộ this example về cách thực sự triển khai giao diện ISynchronizeInvoke.

2

Bạn cũng có thể sử dụng lock để triển khai lớp an toàn chủ đề JobManager theo dõi tiến trình về các chuỗi công việc khác nhau. Trong ví dụ này, tôi chỉ duy trì chuỗi chủ đề công việc đang hoạt động, nhưng điều này có thể được mở rộng cho các báo cáo tiến trình của bạn cần.

class JobManager 
{ 
    private object synchObject = new object(); 

    private int _ActiveJobCount; 

    public int ActiveJobsCount 
    { 
     get { lock (this.synchObject) { return _ActiveJobCount; } } 
     set { lock (this.synchObject) { _ActiveJobCount = value; } } 
    } 

    public void Start(Action job) 
    { 
     var timer = new System.Timers.Timer(1000); 

     timer.Elapsed += (sender, e) => 
     { 
      this.ActiveJobsCount++; 
      job(); 
      this.ActiveJobsCount--; 
     }; 

     timer.Start(); 
    } 
} 

Ví dụ:

class Program 
{ 
    public static void Main(string[] args) 
    { 
     var manager = new JobManager(); 

     manager.Start(() => Thread.Sleep(3500)); 

     while (true) 
     { 
      Console.WriteLine(manager.ActiveJobsCount); 

      Thread.Sleep(250); 
     } 
    } 
} 
Các vấn đề liên quan