2015-10-08 16 views
5

Chúng tôi có một phương pháp REST API tương tự như:Tạm dừng REST của đồng thời gọi cho đến khi người đầu tiên hoàn thành

List<item> GetItems(int AccountID) 
{ 
    var x = getFromCache(AccountID); 
    if(x==null) 
    { 
     x = getFromDatabase(AccountID); 
     addToCache(AccountID, x); 
    } 
    return x; 
} 

Đây là một phương pháp khá tốn kém với một số DB phức tạp gọi, và chúng tôi có một tình huống phổ biến nơi hàng trăm người dùng với cùng một AccountId sẽ thực hiện cuộc gọi gần như đồng thời (tất cả chúng đều được thông báo bằng một chương trình phát sóng).

Trong phương pháp, chúng tôi lưu vào bộ kết quả trong 10 giây vì kết quả gần thời gian là tốt cho mọi người đưa ra yêu cầu trong cửa sổ đó. Tuy nhiên, vì tất cả chúng đều thực hiện cuộc gọi cùng một lúc (một lần nữa, đối với một AccountID cụ thể), bộ đệm không bao giờ được điền trước, vì vậy mọi người sẽ kết thúc cuộc gọi cơ sở dữ liệu. Vì vậy, câu hỏi của tôi là, trong phương thức, làm thế nào tôi có thể tạm dừng tất cả các yêu cầu đến cho một tài khoản cụ thểId và khiến tất cả chờ kết quả đầu tiên hoàn thành, để phần còn lại của cuộc gọi có thể sử dụng bộ nhớ cache kết quả được đặt?

Tôi đã đọc một chút về Monitor.Pulse và Monitor.Lock nhưng việc triển khai loại khóa cho mỗi loại tài khoản thoát khỏi tôi. Bất kỳ trợ giúp nào sẽ được đánh giá cao.

+0

là có một lý do tại sao nhiều người sử dụng đang sử dụng AccountID cùng là một ID tài khoản dịch vụ ..? bạn có thể thay đổi thủ tục lưu trữ của bạn để sử dụng các giao dịch hoặc thêm một số lệnh 'With No Lock' vào phía cơ sở dữ liệu giả sử bạn đang sử dụng Sql Server ..? – MethodMan

+0

Tôi sẽ xem xét bộ nhớ cache cấp 2 vì vậy nếu bạn có bộ nhớ cache thứ hai "đang chờ xử lý" kéo dài 10 20 giây thì mọi cuộc gọi hiện có trong bộ nhớ cache đang chờ xử lý sẽ tự chặn và chờ một chút trước khi thực hiện cuộc gọi db thực. Bạn có thể tương tự như bộ nhớ cache "chứng minh không tồn tại" cho một lý do tương tự. Tôi sẽ tránh theo dõi và khóa dựa trên giá trị của các biến. Tôi sẽ dự trữ những khóa và đồng bộ hóa cho mã và bộ nhớ bất kể giá trị của các biến. –

Trả lời

2

Bạn phải khóa trên cùng một đối tượng cho các yêu cầu có cùng AccountId nhưng sử dụng đối tượng khác nhau cho từng AccountId riêng lẻ. Dưới đây là ví dụ về cách sử dụng Dictionary để theo dõi đối tượng khóa cho các AccountIds riêng lẻ.

Dictionary<int, Object> Locks = new Dictionary<int, object>(); 

    List<item> GetItems(int AccountID) 
    { 
     //Get different lock object for each AccountId 
     Object LockForAccountId = GetLockObject(AccountID); 

     //block subsequent threads until first thread fills the cache 
     lock (LockForAccountId) 
     { 
      var x = getFromCache(AccountID); 
      if (x == null) 
      { 
       x = getFromDatabase(AccountID); 
       addToCache(AccountID, x); 
      } 
      return x; 
     } 
    } 

    private Object GetLockObject(int AccountID) 
    { 
     Object LockForAccountId; 

     //we must use global lock while accessing dictionary with locks to prevent multiple different lock objects to be created for the same AccountId 
     lock (Locks) 
     { 
      if (!Locks.TryGetValue(AccountID, out LockForAccountId)) 
      { 
       LockForAccountId = new Object(); 
       Locks[AccountID] = LockForAccountId; 
      } 
     } 
     return LockForAccountId; 
    } 
0

Bạn đã nghĩ đến việc sử dụng Lazy<T> cho điều này?

Hãy thử mã này:

private object _gate = new object(); 
List<item> GetItems(int AccountID) 
{ 
    lock (_gate) 
    { 
     var x = getFromCache(AccountID); 
     if (x == null) 
     { 
      x = new Lazy<List<item>>(() => getFromDatabase(AccountID)); 
      addToCache(AccountID, x); 
     } 
     return x.Value; 
    } 
} 

Bạn sẽ cần phải thay đổi getFromCache & addToCache để có chữ ký sau đây:

Lazy<List<item>> getFromCache(int AccountID) 
void addToCache(int AccountID, Lazy<List<item>> x) 
Các vấn đề liên quan