Bạn phải hiểu một số nội bộ ở đây trước tiên. Đối với mỗi người dùng [x], ASP.Net sẽ sử dụng một quy trình công nhân duy nhất. Một quy trình công nhân chứa nhiều luồng. Nếu bạn đang sử dụng nhiều phiên bản trên đám mây, nó thậm chí còn tồi tệ hơn bởi vì sau đó bạn cũng có nhiều trường hợp máy chủ (tôi cho rằng đây không phải là trường hợp).
Một vài vấn đề ở đây:
- Bạn có nhiều người sử dụng và do đó nhiều chủ đề.
- Nhiều chủ đề có thể bế tắc nhau bằng cách ghi các tệp.
- Bạn có nhiều tên miền ứng dụng và do đó có nhiều quy trình.
- Nhiều quá trình có thể khóa ra nhau
Mở và khóa file
File.Open
có một vài lá cờ cho khóa. Về cơ bản, bạn có thể khóa các tệp độc quyền cho mỗi quy trình, đây là một ý tưởng hay trong trường hợp này. Cách tiếp cận hai bước với Exists
và Open
sẽ không hữu ích, bởi vì ở giữa một quy trình công nhân khác có thể làm điều gì đó. Về cơ bản, ý tưởng là gọi Open
với quyền truy cập không độc quyền và nếu không thành công, hãy thử lại với một tên tệp khác.
Điều này về cơ bản giải quyết được vấn đề với nhiều quy trình.
Viết từ nhiều luồng
truy cập File là đơn luồng. Thay vì viết nội dung của bạn vào một tệp, bạn có thể muốn sử dụng một chuỗi riêng biệt để thực hiện truy cập tệp và nhiều luồng cho biết điều cần viết.
Nếu bạn có nhiều yêu cầu đăng nhập hơn bạn có thể xử lý, bạn đang ở trong khu vực không đúng cách. Trong trường hợp đó, cách tốt nhất để xử lý nó để ghi nhật ký IMO là chỉ cần thả dữ liệu. Nói cách khác, làm cho trình ghi nhật ký phần nào mất mát để làm cho cuộc sống tốt hơn cho người dùng của bạn. Bạn có thể sử dụng hàng đợi cho điều đó.
Tôi thường sử dụng ConcurrentQueue
cho chủ đề này và một chuỗi riêng biệt hoạt động với tất cả dữ liệu đã đăng nhập.
này về cơ bản là làm thế nào để làm điều này:
// Starts the worker thread that gets rid of the queue:
internal void Start()
{
loggingWorker = new Thread(LogHandler)
{
Name = "Logging worker thread",
IsBackground = true,
Priority = ThreadPriority.BelowNormal
};
loggingWorker.Start();
}
Chúng ta cũng cần một cái gì đó để làm công việc thực tế và một số biến được chia sẻ:
private Thread loggingWorker = null;
private int loggingWorkerState = 0;
private ManualResetEventSlim waiter = new ManualResetEventSlim();
private ConcurrentQueue<Tuple<LogMessageHandler, string>> queue =
new ConcurrentQueue<Tuple<LogMessageHandler, string>>();
private void LogHandler(object o)
{
Interlocked.Exchange(ref loggingWorkerState, 1);
while (Interlocked.CompareExchange(ref loggingWorkerState, 1, 1) == 1)
{
waiter.Wait(TimeSpan.FromSeconds(10.0));
waiter.Reset();
Tuple<LogMessageHandler, string> item;
while (queue.TryDequeue(out item))
{
writeToFile(item.Item1, item.Item2);
}
}
}
Về cơ bản mã này cho phép bạn làm việc đi tất cả các mục từ một chuỗi đơn sử dụng hàng đợi được chia sẻ qua các chuỗi. Lưu ý rằng ConcurrentQueue
không sử dụng khóa cho TryDequeue
, vì vậy, khách hàng sẽ không cảm thấy đau vì điều này.
Điều cuối cùng cần thiết là thêm nội dung vào hàng đợi. Đó là phần dễ dàng:
public void Add(LogMessageHandler l, string msg)
{
if (queue.Count < MaxLogQueueSize)
{
queue.Enqueue(new Tuple<LogMessageHandler, string>(l, msg));
waiter.Set();
}
}
Mã này sẽ được gọi từ nhiều chuỗi. Nó không đúng 100% vì Count
và Enqueue
không nhất thiết phải được gọi theo cách nhất quán - nhưng với mục đích và mục đích của chúng tôi thì nó đủ tốt. Nó cũng không khóa trong các Enqueue
và waiter
sẽ đảm bảo rằng các công cụ được loại bỏ bởi các chủ đề khác.
Kết hợp tất cả điều này theo mẫu đơn, thêm một số logic vào nó và vấn đề của bạn sẽ được giải quyết.
tốt, tôi không chắc chắn, nhưng dự án đã được triển khai (hơn 200.000 khách hàng) –