2 Câu hỏi: Gọi một phương thức sau khi ThreadPool.QueueUserWorkItem kết thúc

câu hỏi được tạo ra tại Wed, May 8, 2019 12:00 AM

Tôi đang làm việc trên một ứng dụng bảng điều khiển được viết bằng c #

Mục đích của ứng dụng này là đi qua tất cả các ổ đĩa và tệp và làm một cái gì đó trên chúng. Nhưng đi qua tất cả các tệp với một luồng là một quá trình tốn thời gian không phải là mục tiêu của tôi.

Vì vậy, tôi đã quyết định sử dụng ThreadPool để xử lý như vậy:

class Program () {
    static void Main(string[] args) {
        foreach (var d in DriveInfo.GetDrives()) {
            ThreadPool.QueueUserWorkItem(x => Search(d.RootDirectory.GetDirectories()));
        }

        Console.WriteLine("Job is done.");
        Console.ReadKey();
    }

    private static void Search(DirectoryInfo[] dirs) {
        foreach (var dir in dirs) {
            try {
                foreach (var f in dir.GetFiles()) {
                    ThreadPool.QueueUserWorkItem(x => DoTheJob(f));
                }

                ThreadPool.QueueUserWorkItem(x => Search(dir.GetDirectories()));
            } catch (Exception ex) {
                continue;
            }
        }
    }       
}

Vấn đề là Console.WriteLine("Job is done.") thực thi trước khi tất cả các luồng được thực hiện. Tôi đã đọc một số câu hỏi và câu trả lời nhưng không ai trong số họ giải quyết vấn đề của tôi.

Làm cách nào tôi có thể gọi một phương thức sau khi tất cả các luồng trong ThreadPool hoàn thành công việc của họ?

Lưu ý: Như bạn có thể biết, tôi không biết có bao nhiêu chủ đề sẽ được tạo bởi vì tôi không biết có bao nhiêu tệp ở đó. Và thiết lập thời gian chờ không phải là một tùy chọn.

    
1
2 Câu trả lời                              2                         

Sử dụng QueueUserWorkItem () là cách tiếp cận barebones cấp thấp. Không kiểm soát được công việc của bạn, đó là lửa và quên đi.

Task chạy trên đầu ThreadPool và async/await có thể giải quyết vấn đề của bạn tại đây.

toplevel:

var tasks = new List<Task>();
foreach (var d in DriveInfo.GetDrives())
{
    tasks.Add( Search(d.RootDirectory.GetDirectories()));
}
Task.WaitAll(tasks.ToArray());

và sau đó bạn Tìm kiếm () trở thành

private static async Task Search(DirectoryInfo[] dirs)
{
    ... 
    foreach(...)
    {
        await Task.Run(...);
    } 
    await Search(dir.GetDirectories());
}

Điều đó DoTheJob () nên sử dụng I /O không đồng bộ nhưng nếu không, bạn có thể await Task.Run( () => DoTheJob(f))

    
1
2019-05-08 17: 36: 34Z
  1. Vì vậy, nếu DoTheJob không phải là một phương thức không đồng bộ, đây có thể là một nút cổ chai không?
    2019-05-08 16: 15: 27Z
  2. Sử dụng giải pháp được cung cấp của bạn mất nhiều thời gian, có vẻ như là một công việc chủ đề duy nhất. Không có sự trợ giúp của các tác vụ và luồng, phải mất khoảng 8 phút để duyệt qua tất cả các tệp, nhưng sử dụng ThreadPool thì mất khoảng 1 phút.
    2019-05-08 16: 19: 04Z
  3. Không có API async để nhận danh sách các đối tượng hệ thống tệp trong một thư mục. Mặt khác, Task.Run không có kiểm soát giới hạn trên có thể dễ dàng dẫn đến áp lực nhóm luồng với số lượng lớn các đối tượng hệ thống tệp. Đề xuất của tôi sẽ là sử dụng API Parallel được định hướng dữ liệu và kiểm soát song song hoặc TPL Dataflow phù hợp hơn với các luồng không tầm thường có thể ngụ ý đệ quy.
    2019-05-08 16: 42: 06Z
  4. @ Hooman, để sử dụng hiệu quả Parallel.ForEach, nhiệm vụ đầu tiên cần được giải quyết là loại bỏ đệ quy bằng cách nào đó.
    2019-05-08 17: 06: 25Z
  5. Đó là trên ... (-: Nhưng tôi sẽ chỉnh sửa nó trong.
    2019-05-08 17: 35: 43Z

Đây là ví dụ về cách bạn có thể sử dụng Parallel.ForEach để tạo ra tải công bằng:

static IEnumerable<FileSystemInfo> GetFileSystemObjects(DirectoryInfo dirInfo)
{
    foreach (var file in dirInfo.GetFiles())
        yield return file;

    foreach (var dir in dirInfo.GetDirectories())
    {
        foreach (var fso in GetFileSystemObjects(dir))
            yield return fso;
        yield return dir;
    }
}

static void Main(string[] args)
{
    var files = GetFileSystemObjects(new DirectoryInfo(<some path>)).OfType<FileInfo>();

    Parallel.ForEach(files, f =>
    {
        DoTheJob(f);
    });
}
.     
0
2019-05-08 19: 04: 59Z
DoTheJob
nguồn đặt đây
Những câu hỏi khác
2
Ajax đăng dữ liệu trống
yêu cầu 4 tháng trước