2016-09-01 1 views
0

나는 C# asp .net을 사용하여 경매 웹 응용 프로그램을 만들고 있습니다.C# ASP .NET : 코드를보다 동시 적으로 변환하는 방법은 무엇입니까?

내 웹 응용 프로그램은 데이터베이스에서 충돌을 일으킬 수 있기 때문에 동시에 동기화 할 수있는 두 가지 방법이 있습니다. 따라서 이들을 보호하기 위해 잠금 (개체) 코드 블록 만 사용했습니다. 2 가지 방법.

전체 솔루션을 잠그는이 솔루션이 어떤 순간에는 응용 프로그램을 매우 느리게 만들기 때문에 더 병행해야합니다. 나는 서버와 클라이언트 사이에 알림을 보낼 SignalR 라이브러리를 사용했습니다

MyRegistry 클래스는 초마다 틱 타이머를 만들고 온라인 고객에게 알림을 보낼 수 있습니다.

제 생각에는 데이터베이스에 액세스 할 수있는 코드 줄과 알림을 보내는 signalR 메서드는 시스템 속도가 느려지는 줄입니다. ,

public static class mutex 
{ 
    public static string lockObject = "MutEx"; 
} 

내 허브 (SignalR)

public class MyHub : Hub 
{ 

    private static Dictionary<string, string> hashUsersConnIds = new Dictionary<string, string>(512); 
    private IEP_Model db = new IEP_Model(); 
    public readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 

    // Server Hub 
    public void Send(long IDAuc, string lastBidderEmail) 
    { 

     AspNetUser user = db.AspNetUsers.SingleOrDefault(r => r.Email == lastBidderEmail); 

     lock (mutex.lockObject) 
     { 

      auction auction = db.auctions.SingleOrDefault(a => a.IDAuc == IDAuc); 
      long productNewPrice = auction.price + auction.increment + 1; 

      // User can bid! 
      if ((productNewPrice <= user.TokenNumber)) 
      { 
       long newBidderCount = user.TokenNumber - productNewPrice; 
       user.TokenNumber = newBidderCount; 
       db.Entry(user).State = EntityState.Modified; 

       bid newBid = new bid(); 
       newBid.Id = user.Id; 
       newBid.IDAuc = auction.IDAuc; 
       newBid.bidTime = DateTime.Now; 
       newBid.tokens = productNewPrice; 
       db.bids.Add(newBid); 

       // Return tokens to previous client 
       var previousBidder = from o 
             in db.UserAuctionInvested 
             where o.AuctionId == auction.IDAuc 
             select o.UserId; 

       string previousBidderID = previousBidder.SingleOrDefault(); 
       AspNetUser previous = db.AspNetUsers.Find(previousBidderID); 

       // Previous bidder exists 
       if (previousBidderID != null) 
       { 
        long tokensToReturn = (long)db.UserAuctionInvested.Where(u => u.AuctionId == auction.IDAuc && u.UserId == previousBidderID).SingleOrDefault().TokenInvested; 
        long newTokenCount = previous.TokenNumber + tokensToReturn; 
        previous.TokenNumber = newTokenCount; 

        // PUSH NOTIFICATION: SET PREVIOUS CLIENT'S TOKEN NUMBER 
        var clientSelector = "token" + previous.Email.Replace("@", "_"); 
        var clientAlertSelector = "alertToken" + previous.Email.Replace("@", "_"); 
        var warningAlertSelector = "warning" + previous.Email.Replace("@", "_"); 
        // PREVIOUS CLIENT IS ONLINE 
        if (hashUsersConnIds.ContainsKey(previous.Email) && previous.Email != lastBidderEmail) 
        { 
         Clients.Client(hashUsersConnIds[previous.Email]).setTokenNumber(clientSelector, newTokenCount, clientAlertSelector, warningAlertSelector, auction.product_name); 
        } 
        // PUSH NOTIFICATION: CURRENT CLIENT (LAST BIDDER) NEW TOKEN COUNT 
        clientSelector = "token" + lastBidderEmail.Replace("@", "_"); 
        clientAlertSelector = "alertToken" + lastBidderEmail.Replace("@", "_"); 
        warningAlertSelector = "warning" + previous.Email.Replace("@", "_"); 
        if (previous.Email != lastBidderEmail && hashUsersConnIds.ContainsKey(lastBidderEmail)) { 
         Clients.Client(hashUsersConnIds[lastBidderEmail]).setTokenNumber(clientSelector, newBidderCount, clientAlertSelector, "x", "x"); 
        } 
        db.Entry(previous).State = EntityState.Modified; 
        UserAuctionInvested uaiPrevious = db.UserAuctionInvested.Find(previousBidderID, auction.IDAuc); 
        db.UserAuctionInvested.Remove(uaiPrevious); 
       } 
       // This client is first bidder 
       else 
       { 
        var clientSelector = "token" + lastBidderEmail.Replace("@", "_"); 
        var clientAlertSelector = "alertToken" + lastBidderEmail.Replace("@", "_"); 
        // PUSH NOTIFICATION: CURRENT CLIENT (LAST BIDDER) NEW TOKEN COUNT 
        if (hashUsersConnIds.ContainsKey(lastBidderEmail)) 
        { 
         Clients.Client(hashUsersConnIds[lastBidderEmail]).setTokenNumber(clientSelector, newBidderCount, clientAlertSelector, "x", "x"); 
        } 
       } 

       // Creating new bid userAuctionInvested -> [dbo].[userAuctionInvested] (user, auction, tokenNo) 
       UserAuctionInvested uai = new UserAuctionInvested(); 
       uai.AuctionId = auction.IDAuc; 
       uai.UserId = user.Id; 
       uai.TokenInvested = productNewPrice; 
       db.UserAuctionInvested.Add(uai); 

       // Update Auction 
       auction.increment += 1; 
       auction.lastbidder = lastBidderEmail; 

       // Seconds to end of auction 
       var secondsDifference = ((DateTime)auction.close_date_time - DateTime.Now).TotalSeconds; 
       if (secondsDifference <= 10) 
       { 
        DateTime oldCloseDateTime = (DateTime)auction.close_date_time; 
        DateTime newCloseDateTime = oldCloseDateTime.AddSeconds(10); 
        auction.close_date_time = newCloseDateTime; 
        auction.duration += 10; 
       } 

       db.Entry(auction).State = EntityState.Modified; 
       string remainingToEnd = ((DateTime)auction.close_date_time - DateTime.Now).ToString(@"dd\:hh\:mm\:ss"); 
       Clients.All.clientBidsUpdate(IDAuc, auction.state, remainingToEnd, lastBidderEmail, auction.price + auction.increment, "false"); 
       // Update details auction page // clientWarningSelector, auctionNameWarning 
       Clients.All.auctionDetailsUpdate(IDAuc, lastBidderEmail, auction.price + auction.increment, newBid.bidTime.ToString(@"dd\:hh\:mm\:ss"), "Open"); 
       db.SaveChanges(); 
       return; 

      } 
      // Client was previous bidder needs to pay +1 on actual price 
      else if (auction.lastbidder == user.Email) 
      { 
       if (user.TokenNumber > 0) // can place next bid 
       { 
        user.TokenNumber = user.TokenNumber - 1; 
        db.Entry(user).State = EntityState.Modified; 
        bid newBid = new bid(); 
        newBid.Id = user.Id; 
        newBid.IDAuc = auction.IDAuc; 
        newBid.bidTime = DateTime.Now; 
        newBid.tokens = auction.price + auction.increment + 1; 
        db.bids.Add(newBid); 
        if (hashUsersConnIds.ContainsKey(lastBidderEmail)) 
        { 
         var clientSelector = "token" + lastBidderEmail.Replace("@", "_"); 
         var clientAlertSelector = "alertToken" + lastBidderEmail.Replace("@", "_"); 
         var clientWarningSelector = "warning" + lastBidderEmail.Replace("@", "_"); 
         Clients.Client(hashUsersConnIds[lastBidderEmail]).setTokenNumber(clientSelector, user.TokenNumber, clientAlertSelector, "x", "x"); 
        } 
        // Updating userAuctionInvested -> [dbo].[userAuctionInvested] (user, auction, tokenNo) 
        UserAuctionInvested uai = db.UserAuctionInvested.Where(u => u.AuctionId == auction.IDAuc && u.UserId == user.Id).SingleOrDefault(); 
        uai.TokenInvested += 1; 
        db.Entry(uai).State = EntityState.Modified; 
        // Update Auction 
        auction.increment += 1; 
        // Seconds to end of auction 
        var secondsDifference = ((DateTime)auction.close_date_time - DateTime.Now).TotalSeconds; 
        if (secondsDifference <= 10) 
        { 
         DateTime oldCloseDateTime = (DateTime)auction.close_date_time; 
         DateTime newCloseDateTime = oldCloseDateTime.AddSeconds(10); 
         auction.close_date_time = newCloseDateTime; 
         auction.duration += 10; 
        } 
        db.Entry(auction).State = EntityState.Modified; 
        string remainingToEnd = ((DateTime)auction.close_date_time - DateTime.Now).ToString(@"dd\:hh\:mm\:ss"); 
        Clients.All.clientBidsUpdate(IDAuc, auction.state, remainingToEnd, lastBidderEmail, auction.price + auction.increment, "false"); 
        Clients.All.auctionDetailsUpdate(IDAuc, lastBidderEmail, auction.price + auction.increment, newBid.bidTime.ToString(@"dd\:hh\:mm\:ss"), "Open"); 

        db.SaveChanges(); 
        return; 
       } 
      } 
      // No tokens - Warn user! 
      string remaining = ((DateTime)auction.close_date_time - DateTime.Now).ToString(@"dd\:hh\:mm\:ss"); 
      Clients.All.clientBidsUpdate(IDAuc, auction.state, remaining, lastBidderEmail, auction.price + auction.increment, "true"); 
     } 
    } 

    // Registring client 
    public void registerConId(string email) 
    { 
     hashUsersConnIds[email] = Context.ConnectionId; 
    } 

} 

그리고 마지막으로 여기에 내 웹 응용 프로그램에 타이머와 같은 역할을 내 레지스트리 클래스입니다 :

여기에만 잠금 개체로 사용되는 내 객체의 1 초마다 틱하고 데이터베이스를 업데이트합니다.

public class MyRegistry : Registry 
{ 

    public readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 

    public MyRegistry() 
    { 

     Schedule(() => 
     { 

      // Check if some auction is over each 1 sec 
      lock (mutex.lockObject) 
      { 

       IEP_Model db = new IEP_Model(); 
       var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>(); 

       var auctions = from o 
           in db.auctions 
           where o.state == "Open" 
           select o; 

       var auctionsList = auctions.ToList(); 

       foreach (var auction in auctionsList) 
       { 

        DateTime now = DateTime.Now; 
        DateTime end = (DateTime)auction.close_date_time; 

        // Time is up! Auction is "Expired" or "Sold" 
        if (now >= end) 
        { 
         var edited = db.auctions.Find(auction.IDAuc); 
         // NO Winner 
         if (edited.increment == 0) 
         { 
          edited.state = "Expired"; 
          db.Entry(edited).State = EntityState.Modified; 
          TimeSpan timeEnd = new TimeSpan(0); 
          string newDurationExpired = timeEnd.ToString(@"dd\:hh\:mm\:ss"); 

          hubContext.Clients.All.timerUpdate(auction.IDAuc, edited.state, newDurationExpired, " - ", edited.price, "false", "odd", "true"); 
          db.SaveChanges(); 
         } 
         // YES Winner 
         else 
         { 
          edited.state = "Sold"; 
          db.Entry(edited).State = EntityState.Modified; 
          db.SaveChanges(); 

          // Refresh client 
          long soldPrice = edited.price + edited.increment; 
          TimeSpan timeEnd = new TimeSpan(0); 
          string newDurationSold = timeEnd.ToString(@"dd\:hh\:mm\:ss"); 
          hubContext.Clients.All.timerUpdate(auction.IDAuc, edited.state, newDurationSold, edited.lastbidder, soldPrice, "false", "odd", "true"); 

          // Return tokens to non-winners 
          string winnerId = db.AspNetUsers.Where(a => a.Email == edited.lastbidder).SingleOrDefault().Id; 
          //var bids = db.bids.Where(b => b.IDAuc == auction.IDAuc && b.Id != winnerId).ToList(); 
          var userInvested = db.UserAuctionInvested.Where(i => i.AuctionId == auction.IDAuc); 

          foreach (var item in userInvested.ToList()) 
          { 
           if (item.UserId != winnerId) // Not winner - return money 
           { 
            AspNetUser FetchUser = db.AspNetUsers.Find(item.UserId); 
            FetchUser.TokenNumber += (long)item.TokenInvested; 
            db.Entry(FetchUser).State = EntityState.Modified; 
           } 
           db.UserAuctionInvested.Remove(item); 
          } 

          db.SaveChanges(); 

         } 
        } 

        // Update auction - is still active 
        long actualPrice = auction.price + auction.increment; 
        TimeSpan difference = end.Subtract(now); 
        string newDuration = difference.ToString(@"dd\:hh\:mm\:ss"); 

        string lastTenSeconds__ = "false"; 
        string numberOddEven__ = "odd"; 
        string end__ = "false"; 
        if (difference.TotalSeconds <= 10) 
        { 
         lastTenSeconds__ = "true"; 
         if (difference.TotalSeconds <= 1) 
         { 
          end__ = "true"; 
         } 
         if ((int)Math.Ceiling(difference.TotalSeconds) % 2 == 0) 
         { 
          numberOddEven__ = "even"; 
         } 
        } 
        hubContext.Clients.All.timerUpdate(auction.IDAuc, auction.state, newDuration, auction.lastbidder, actualPrice, lastTenSeconds__, numberOddEven__, end__); 


       } 

      } 
     }).ToRunNow().AndEvery(1).Seconds(); 
    } 

} 

아이디어를 얻으려면 도움이 필요합니다. 어떻게하면이 코드를 조금 변형시킬 수 있을까요? 나는 C#이 모니터를 지원한다는 것을 보았는데,이 모니터를 사용하면 도움이 될지 궁금해하니?

+1

(DB 등) 원격 리소스에 대한 액세스를 통해 잠금의 모든 종류의 (매우 부서지기 쉬운 일뿐만 아니라) 동시성을 죽일 것이다 :

당신이 인 Pluralsight이 있다면 나는이 과정을 건의 할 것입니다. 당신이 찾고 있어야하는 가장 가능성있는 솔루션은 DB를 재 설계하여 손상을 방지하고 앱 수준에서 업데이트 실패를 처리하는 것입니다. –

답변

0

코드를 보면 가장 먼저 db 전화를 가능한 한 줄이고 현재 경매를로드하고 로그인 한 사용자 및 기타 상당히 정적 인 것을 줄이는 것이 좋습니다. Redis (메모리 캐싱 도구에서) 또는 사전과 같은 것입니다. (이진 검색 성능을 위해)

두 번째로 나는 라이브 경매 추적을 위해 ConcurrentDictionary를 사용하는 멀티 스레드 프로세스를 제안하고, 원하는 속도를 가지고 있으며 필요할 경우 BlockingCollection에 래핑 할 수 있습니다 변경을 대기하는 thread 그렇게하면 자물쇠를 사용할 필요가 없습니다. 또한 대기열을 사용하여 데이터를 저장소에 지속 적으로로드 해제 할 수 있습니다.

https://app.pluralsight.com/library/courses/csharp-concurrent-collections/table-of-contents

관련 문제