2010-03-26 34 views
13

Tôi đã đọc Threading from within a class with static and non-static methods và tôi đang ở trong một tình huống tương tự.Mã tĩnh chạy với nhiều chủ đề như thế nào?

Tôi có phương pháp tĩnh để lấy dữ liệu từ tài nguyên và tạo một số đối tượng thời gian chạy dựa trên dữ liệu.

static class Worker{ 
    public static MyObject DoWork(string filename){ 
     MyObject mo = new MyObject(); 

     // ... does some work 

     return mo; 
    } 
} 

Phương pháp mất một thời gian (trong trường hợp này là đọc tệp 5-10MB) và trả về một đối tượng.

Tôi muốn sử dụng phương pháp này và sử dụng phương pháp này trong tình huống nhiều luồng để tôi có thể đọc nhiều tệp cùng một lúc. Thiết kế các vấn đề/hướng dẫn sang một bên, làm thế nào nhiều chủ đề có thể truy cập mã này?

Hãy nói rằng tôi có một cái gì đó như thế này ...

class ThreadedWorker { 
    public void Run() { 
     Thread t = new Thread(OnRun); 
     t.Start(); 
    } 

    void OnRun() { 
     MyObject mo = Worker.DoWork("somefilename"); 

     mo.WriteToConsole(); 
    } 
} 

Liệu các phương pháp chạy tĩnh cho mỗi chủ đề, cho phép thực hiện song song?

+0

Cảm ơn tất cả các bạn đã trả lời, tất cả đều tuyệt vời! – Krisc

Trả lời

19

Có, phương pháp sẽ có thể chạy tốt trong nhiều chuỗi. Điều duy nhất bạn nên lo lắng là truy cập cùng một tệp trong nhiều luồng cùng một lúc.

3

Nếu phương pháp tĩnh được viết thành luồng an toàn, sau đó nó có thể được gọi từ bất kỳ chuỗi nào hoặc thậm chí được chuyển đến một nhóm luồng.

Bạn phải ghi nhớ - Đối tượng .NET không trực tiếp trên chủ đề (ngoại trừ cấu trúc nằm trên ngăn xếp của chuỗi) - đường dẫn thực hiện. Vì vậy, nếu một thread có thể truy cập một thể hiện của một đối tượng, nó có thể gọi một phương thức instance. Bất kỳ thread nào cũng có thể gọi một phương thức tĩnh bởi vì tất cả những gì cần biết là kiểu của đối tượng.

8

Bạn nên phân biệt giữa các phương thức tĩnh và trường tĩnh trong trường hợp này. Mỗi cuộc gọi đến một phương thức tĩnh sẽ có "bản sao" riêng của phương thức và các biến cục bộ của nó. Điều đó có nghĩa là trong mẫu của bạn, mỗi cuộc gọi sẽ hoạt động trên cá thể MyObject của riêng nó và các cuộc gọi sẽ không liên quan gì đến nhau. Điều này cũng có nghĩa là không có vấn đề với việc thực hiện chúng trên các chủ đề khác nhau.

+1

bản sao của các biến cục bộ, vâng. bản sao của phương pháp, không, nhưng nó thực sự không quan trọng vì nhiều chủ đề đồng thời có thể đọc cùng một khối bộ nhớ mà không có bất kỳ vấn đề nào. –

+0

@Fredrik Mork Câu trả lời của bạn thực sự tốt. Tôi có một câu hỏi. Nếu giả sử phương thức tĩnh nằm trong lớp tĩnh thì mọi lời gọi tới phương thức cũng sẽ tạo một bản sao của lớp nghĩa là sao chép tất cả các phương thức tĩnh trong nó hoặc chỉ một phương thức tĩnh này. –

1

Phương pháp tĩnh sẽ chạy trên chuỗi mà bạn gọi từ đó. Miễn là chức năng của bạn là tái nhập, có nghĩa là thực thi có thể nhập lại một cách an toàn chức năng trong khi thực hiện từ một luồng khác (hoặc tiếp tục lên ngăn xếp) đã có trong hàm.

Vì chức năng của bạn là tĩnh, bạn không thể truy cập vào các biến thành viên, đây sẽ là một cách khiến cho nó không được tái nhập. Nếu bạn có một biến địa phương tĩnh duy trì trạng thái, đó sẽ là một cách khác để làm cho nó không tái nhập.

Mỗi khi bạn nhập, bạn tạo một MyObject mới, do đó, mỗi bit luồng thực thi sẽ xử lý với cá thể MyObject riêng của nó, điều này rất tốt. Nó có nghĩa là họ sẽ không cố gắng truy cập cùng một đối tượng cùng một lúc (điều này sẽ dẫn đến điều kiện chủng tộc).

Điều duy nhất bạn đang chia sẻ giữa nhiều cuộc gọi chính là Bảng điều khiển. Nếu bạn gọi nó trên nhiều luồng, chúng sẽ xuất ra trên giao diện điều khiển. Và bạn có khả năng có thể hành động trên cùng một tệp (trong ví dụ của bạn tên tệp được mã hóa cứng), nhưng có thể bạn sẽ hành động trên nhiều tệp. Các chuỗi liên tiếp có thể không mở được tệp nếu các tệp trước đó mở nó.

2

Một điều bạn nên ghi nhớ khi thực thi các phương thức tĩnh đồng thời là các trường tĩnh, chỉ tồn tại một lần. Vì vậy, nếu phương thức đọc và viết các trường tĩnh, các vấn đề đồng thời có thể xảy ra.

Tuy nhiên, có một thuộc tính được gọi là ThreadStaticAttribute cho biết rằng đối với mỗi luồng có một trường riêng biệt. Điều này có thể hữu ích trong một số trường hợp cụ thể.

Biến cục bộ là separte cho mỗi chuỗi, vì vậy bạn không cần phải quan tâm đến điều này. Nhưng hãy lưu ý các tài nguyên bên ngoài như các tệp, có thể có vấn đề khi được truy cập đồng thời.

Best Regards,
Oliver Hanappi

2

Bên cạnh những khía cạnh mã, trong đó đã được trả lời, bạn cũng cần phải xem xét các khía cạnh I/O của việc truy cập các tập tin.

Lưu ý về kiến ​​trúc và cách tôi đã hoàn thành nhiệm vụ này trong quá khứ - không gợi ý rằng đây là cách tiếp cận phù hợp hoặc nhất thiết phải phù hợp với ứng dụng của bạn. Tuy nhiên, tôi nghĩ rằng các ghi chú của tôi có thể hữu ích cho quá trình suy nghĩ của bạn:

Thiết lập trường ManualResetEvent, gọi nó là ActivateReader hoặc một cái gì đó tương tự, điều này sẽ rõ ràng hơn. Khởi tạo nó là sai.

Thiết lập trường boolean, gọi nó là TerminateReaderThread. Khởi tạo nó như là sai, một lần nữa điều này sẽ trở nên rõ ràng hơn nữa.

Thiết lập trường Queue < chuỗi >, gọi nó là Tệp và khởi tạo.

Chuỗi ứng dụng chính của tôi kiểm tra xem có khóa trên hàng đợi tệp hay không trước khi viết từng đường dẫn tệp có liên quan vào đó. Khi tệp đã được ghi, sự kiện đặt lại bị vấp chỉ báo cho chuỗi trình đọc hàng đợi có các tệp chưa đọc trong hàng đợi.

Sau đó, tôi thiết lập một chuỗi để hoạt động như một trình đọc hàng đợi. Chủ đề này chờ cho ManualResetEvent bị vấp bằng phương thức WaitAny() - đây là một phương thức chặn mà unblocks một khi ManualResetEvent bị vấp. Khi nó bị vấp, chủ đề sẽ kiểm tra xem có tắt máy chủ [bằng cách kiểm tra trường TerminateReaderThread] hay không. Nếu tắt máy đã được khởi động, luồng sẽ tắt một cách duyên dáng, nếu không nó sẽ đọc mục tiếp theo từ hàng đợi và sinh ra một chuỗi công nhân để xử lý tệp. Sau đó tôi khóa hàng đợi trước khi kiểm tra xem có còn vật phẩm nào không. Nếu không có mục nào còn lại, tôi đặt lại ManualResetEvent sẽ tạm dừng luồng của chúng tôi trên lần tiếp theo. Sau đó tôi mở khóa hàng đợi để chủ đề chính có thể tiếp tục viết cho nó. Mỗi trường hợp của chuỗi công nhân cố gắng lấy khóa độc quyền trên tệp được khởi tạo cho đến khi hết thời gian chờ, nếu khóa thành công, nó xử lý tệp, nếu nó không thành công, nó sẽ thử lại khi cần thiết, một ngoại lệ và tự chấm dứt. Trong trường hợp ngoại lệ, luồng có thể thêm tệp vào cuối hàng đợi để một luồng khác có thể lấy lại nó ở một điểm sau. Hãy lưu ý rằng nếu bạn làm điều này, thì bạn cần phải xem xét vòng lặp vô tận mà một vấn đề đọc I/O có thể gây ra. Trong một sự kiện như vậy, một từ điển của các tập tin không thành công với các quầy của bao nhiêu lần họ đã thất bại có thể hữu ích để nếu một số giới hạn đạt được, bạn có thể ngừng thêm lại tập tin vào cuối hàng đợi.

Khi ứng dụng của tôi quyết định chuỗi trình đọc không còn cần thiết nữa, nó sẽ đặt trường TerminateReaderThread thành true. Lần tới chu trình của trình đọc sẽ bắt đầu quá trình của nó, quá trình tắt máy của nó sẽ được kích hoạt.

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