나는 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#이 모니터를 지원한다는 것을 보았는데,이 모니터를 사용하면 도움이 될지 궁금해하니?
(DB 등) 원격 리소스에 대한 액세스를 통해 잠금의 모든 종류의 (매우 부서지기 쉬운 일뿐만 아니라) 동시성을 죽일 것이다 :
당신이 인 Pluralsight이 있다면 나는이 과정을 건의 할 것입니다. 당신이 찾고 있어야하는 가장 가능성있는 솔루션은 DB를 재 설계하여 손상을 방지하고 앱 수준에서 업데이트 실패를 처리하는 것입니다. –