Tôi đang viết một ứng dụng đơn giản (cho vợ tôi không kém :-P) thực hiện một số thao tác hình ảnh (thay đổi kích cỡ, dấu thời gian, vv) cho một loạt ảnh có khả năng lớn. Vì vậy, tôi đang viết một thư viện có thể làm điều này cả đồng bộ và không đồng bộ. Tôi quyết định sử dụng Event-based Asynchronous Pattern. Khi sử dụng mẫu này, bạn cần phải nâng cao một sự kiện khi công việc đã được hoàn thành. Đây là nơi tôi đang gặp vấn đề khi biết nó được thực hiện. Vì vậy, về cơ bản, trong phương pháp DownsizeAsync của tôi (phương pháp async để giảm kích thước hình ảnh) Tôi đang làm một cái gì đó như thế này:Cách tốt nhất để có nhiều chuỗi làm việc là gì và chờ đợi cho tất cả các chuỗi đó hoàn thành?
public void DownsizeAsync(string[] files, string destination)
{
foreach (var name in files)
{
string temp = name; //countering the closure issue
ThreadPool.QueueUserWorkItem(f =>
{
string newFileName = this.DownsizeImage(temp, destination);
this.OnImageResized(newFileName);
});
}
}
Phần khó hiểu bây giờ là biết khi nào chúng hoàn tất.
Dưới đây là những gì tôi đã cân nhắc: Sử dụng ManualResetEvents như ở đây: http://msdn.microsoft.com/en-us/library/3dasc8as%28VS.80%29.aspx Nhưng vấn đề tôi gặp phải là bạn chỉ có thể chờ 64 sự kiện trở xuống. Tôi có thể có nhiều hơn nhiều hình ảnh.
tùy chọn thứ hai: Có một quầy mà đếm những hình ảnh đó đã được thực hiện, và nâng cao sự kiện khi đếm đạt tổng:
public void DownsizeAsync(string[] files, string destination)
{
foreach (var name in files)
{
string temp = name; //countering the closure issue
ThreadPool.QueueUserWorkItem(f =>
{
string newFileName = this.DownsizeImage(temp, destination);
this.OnImageResized(newFileName);
total++;
if (total == files.Length)
{
this.OnDownsizeCompleted(new AsyncCompletedEventArgs(null, false, null));
}
});
}
}
private volatile int total = 0;
Bây giờ này cảm thấy "hacky" và tôi không hoàn toàn chắc chắn nếu đó là chủ đề an toàn.
Vì vậy, câu hỏi của tôi là, cách tốt nhất để làm điều này là gì? Có cách nào khác để đồng bộ hóa tất cả các chủ đề? Tôi có nên không sử dụng ThreadPool không? Cảm ơn!!
CẬP NHẬT Dựa trên phản hồi trong các ý kiến và từ một vài câu trả lời tôi đã quyết định áp dụng cách này:
Trước tiên, tôi đã tạo ra một phương pháp khuyến nông mà lô một đếm được vào "lô":
public static IEnumerable<IEnumerable<T>> GetBatches<T>(this IEnumerable<T> source, int batchCount)
{
for (IEnumerable<T> s = source; s.Any(); s = s.Skip(batchCount))
{
yield return s.Take(batchCount);
}
}
Về cơ bản, nếu bạn làm điều gì đó như thế này:
foreach (IEnumerable<int> batch in Enumerable.Range(1, 95).GetBatches(10))
{
foreach (int i in batch)
{
Console.Write("{0} ", i);
}
Console.WriteLine();
}
bạn nhận được kết quả này:
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90
91 92 93 94 95
Ý tưởng là (như một người trong nhận xét đã chỉ ra) không cần phải tạo một chuỗi riêng biệt cho mỗi hình ảnh. Vì vậy, tôi sẽ ghép các hình ảnh thành [machine.cores * 2] số lô. Sau đó, tôi sẽ sử dụng phương pháp thứ hai của tôi mà chỉ đơn giản là để giữ một truy cập đi và khi truy cập đạt tổng số tôi mong đợi, tôi sẽ biết tôi đã làm xong.
Lý do Tôi tin bây giờ mà nó là trong thực tế chủ đề an toàn, là bởi vì tôi đã đánh dấu tổng biến như dễ bay hơi mà theo MSDN:
Các modifier dễ bay hơi thường được sử dụng cho một trường được truy cập bởi nhiều chủ đề mà không cần sử dụng câu lệnh khóa để truy cập hàng loạt. Sử dụng modifier ổn định đảm bảo rằng một thread lấy hầu hết các up-to-date giá trị được viết bởi một chủ đề
có nghĩa là tôi phải ở trong rõ ràng (nếu không, xin vui lòng cho tôi biết !!)
Vì vậy, đây là đoạn code tôi đang đi với:
public void DownsizeAsync(string[] files, string destination)
{
int cores = Environment.ProcessorCount * 2;
int batchAmount = files.Length/cores;
foreach (var batch in files.GetBatches(batchAmount))
{
var temp = batch.ToList(); //counter closure issue
ThreadPool.QueueUserWorkItem(b =>
{
foreach (var item in temp)
{
string newFileName = this.DownsizeImage(item, destination);
this.OnImageResized(newFileName);
total++;
if (total == files.Length)
{
this.OnDownsizeCompleted(new AsyncCompletedEventArgs(null, false, null));
}
}
});
}
}
Tôi mở cửa cho thông tin phản hồi như tôi không có cách nào một chuyên gia về xử lý đa luồng, vì vậy nếu có ai nhìn thấy bất kỳ vấn đề với điều này, hoặc có một ý tưởng tốt hơn, xin vui lòng cho tôi biết. (Vâng, đây chỉ là một ứng dụng được tạo ra ở nhà, nhưng tôi có một số ý tưởng về cách tôi có thể sử dụng kiến thức mà tôi có được ở đây để cải thiện dịch vụ Tìm kiếm/Chỉ mục của chúng tôi tại nơi làm việc.) cảm thấy như tôi đang sử dụng cách tiếp cận đúng. Cảm ơn tất cả mọi người đã giúp đỡ của bạn.
tổng số không trông an toàn cho tôi! Phương thức truy cập – RichardOD
có khả năng mở rộng cao. Chỉ cần làm cho nó threadsafe sử dụng Interlocked.Increment và .Decrement .. –
Trong thực tế, là 64 một giới hạn cho điều này? Ngay cả với 64 lõi vật lý, bạn sẽ gặp phải tình trạng tắc nghẽn bộ nhớ và đĩa làm suy giảm nhanh hơn nhiều khi truy cập song song. Nhưng sau đó, chúng có thể biến mất sau một hoặc hai năm. – peterchen