2012-08-24 18 views
8

में समवर्ती संघर्ष को हल करने के लिए कैसे करें: एक परिदृश्य: किसी तालिका में डालने के लिए एक निश्चित मात्रा में डेटा डालने के लिए, जब कोई थ्रेसहोल्ड तक नहीं पहुंचता है, तो मैंने इस परिदृश्य को बहु-थ्रेडेड (उदाहरण के लिए एएसपीनेट) समवर्ती समस्याएं दिखाई दीं।ado.net

मेरा प्रश्न समवर्ती समस्या का हल करने के लिए कैसे, lock मामले का उपयोग नहीं करते

void Main() 
{ 

    Enumerable.Range(0,20).ToList().ForEach(i=>{ 
     MockMulit(); 
    }); 

} 
//Start a certain number of threads for concurrent simulation 
void MockMulit() 
{ 
    int threadCount=100; 

    ClearData();//delete all data for test 


    var tasks=new List<Task>(threadCount); 
    Enumerable.Range(1,threadCount).ToList().ForEach(i=>{ 
    var j=i; 
    tasks.Add(Task.Factory.StartNew(()=>T3(string.Format("Thread{0}-{1}",Thread.CurrentThread.ManagedThreadId,j)))); 
    }); 
    Task.WaitAll(tasks.ToArray()); 

    CountData().Dump();//show that the result 
} 

विधि से एक है - संगामिति बहुत गंभीर

void T1(string name) 
{ 
     using(var conn=GetOpendConn()) 
     { 
      var count=conn.Query<int>(@"select count(*) from dbo.Down").Single(); 
      if(count<20) 
      { 
       conn.Execute(@"insert into dbo.Down (UserName) values (@UserName)",new{UserName=name}); 
      } 
     } 

} 

विधि दो - एसक्यूएल एक साथ रखा कम कर सकते हैं समवर्ती, लेकिन अभी भी मौजूद है

void T2(string name) 
{ 
    using(var conn=GetOpendConn()) 
    { 
     conn.Execute(@" 
         if((select count(*) from dbo.Down)<20) 
         begin 
          --WAITFOR DELAY '00:00:00.100'; 
          insert into dbo.Down (UserName) values (@UserName) 
         end",new{UserName=name}); 
    } 
} 

विधि तीन - लॉक डी के साथ समवर्ती estroy, लेकिन मुझे नहीं लगता कि यह एक सबसे अच्छा समाधान है

private static readonly object countLock=new object(); 
void T3(string name) 
{ 
    lock(countLock) 
    { 
     using(var conn=GetOpendConn()) 
     { 
      var count=conn.Query<int>(@"select count(*) from dbo.Down").Single(); 
      if(count<20) 
      conn.Execute(@"insert into dbo.Down (UserName) values (@UserName)",new{UserName=name}); 
     } 
    } 
} 

अन्य मदद विधि

//delete all data 
void ClearData() 
{ 
    using(var conn=GetOpendConn()) 
    { 
     conn.Execute(@"delete from dbo.Down"); 
    } 
} 
//get count 
int CountData() 
{ 
    using(var conn=GetOpendConn()) 
    { 
     return conn.Query<int>(@"select count(*) from dbo.Down").Single(); 
    } 
} 
//get the opened connection 
DbConnection GetOpendConn() 
{ 
    var conn=new SqlConnection(@"Data Source=.;Integrated Security=SSPI;Initial Catalog=TestDemo;"); 
    if(conn.State!=ConnectionState.Open) 
     conn.Open(); 
    return conn; 
} 
+0

आप किस प्रकार का डेटाबेस उपयोग कर रहे हैं जो सम्मिलित डेटा को संभाल नहीं सकता है? – Jake1164

+0

जिस परिदृश्य के खिलाफ आप रक्षा करने की कोशिश कर रहे हैं वह बहुत अस्पष्ट है। क्या आप शायद जो कुछ करने की कोशिश कर रहे हैं उसे फिर से बदल सकते हैं? –

+0

@MarcGravell वास्तव में मैं अपनी ई-कॉमर्स वेबसाइट के लिए एक सामान स्पाइक गतिविधि तैयार कर रहा था। उदाहरण के लिए, केवल 20 सामान ही खरीद सकते हैं, क्योंकि एएसपीनेट बहु-धागा है, हमारे डेटाबेस में 21 रिकॉर्ड दिखाई देते हैं! :( – JeffZhnn

उत्तर

4

ऐसा लगता है कि आप केवल जब वहाँ कम से कम 20 पंक्तियों नीचे में सम्मिलित करना चाहते हैं। यदि ऐसा है तो: बनाना है कि एक ही आपरेशन:

insert dbo.Down (UserName) 
select @UserName 
where (select count(1) from dbo.Down) < 20 

select @@rowount -- 1 if we inserted, 0 otherwise 

वैकल्पिक रूप से, अगर आप * जरूरत * यदि आप, ताकि आप एक महत्वपूर्ण दूरी ताला पाने के लेन-देन, आदर्श "Serializable" का उपयोग करने की आवश्यकता होगी - शायद प्रारंभिक गणना के लिए (UPDLOCK) भी जोड़ना, यह सुनिश्चित करने के लिए कि यह एक उत्सुक लेखन लॉक (या डेडलॉक्स की बजाय ब्लॉक) लेता है। लेकिन: एकल TSQL आपरेशन (पहले से ही दर्शाया गया है (बेहतर है तुम भी है कि और अधिक पागल बना सकता है, हालांकि मैं यकीन नहीं है कि यह जरूरत है):।

declare @count int 

begin tran 

insert dbo.Down (UserName) 
select @UserName 
where (select count(1) from dbo.Down (UPDLOCK)) < 20 

set @count = @@rowount 

commit tran 

select @count -- 1 if we inserted, 0 otherwise 
+0

मैं एक परीक्षण (https://github.com/dushouke/ConcurrencyTest) लिख रहा था, यह सुनिश्चित करें कि एक ही ऑपरेशन समस्या का समाधान नहीं कर सकता है। लेकिन 'UPDLOCK' के साथ समवर्ती – JeffZhnn

+0

@jefferydu no, ' UPDLOCK' "समवर्ती को नष्ट नहीं करता" - यह केवल *** सही ढंग से *** इंगित करता है कि हम इस डेटा को उसी लेनदेन में बदलने की बजाय योजना बना रहे हैं (क्या इसे पढ़ने के बजाए)। क्या आपने पोस्ट किए गए दूसरे संस्करण को आजमाया था? –

+0

हां, मैंने दूसरे संस्करण का परीक्षण किया, कोई और रिकॉर्ड डाला नहीं गया। परिणाम स्क्रीनशॉट http://i.stack.imgur.com/155ev.jpg – JeffZhnn

1

रिवर्स में ऐसा करने पर विचार करें और यह शायद ज्यादा हो जाएगा । सरल उदाहरण के लिए:

  • एक स्वत: incrementing सूचकांक के साथ एक तालिका बनाएं कॉल यह "ऊपर", या "RequestOrdering", या जो कुछ भी
  • किसी भी क्रम में ग्राहक अनुरोधों जाओ प्रत्येक अनुरोध के लिए:।।।
    • नया डालें ऊपर पंक्ति।
    • अंतिम डाली गई पंक्ति आईडी प्राप्त करें।
    • यदि अंतिम डालने आईडी < = 20, असली डालें।
  • जब आप इसके साथ काम करते हैं तो अपनी अप तालिका को फेंक दें।

यदि आपका डेटाबेस बहु-स्तंभ प्राथमिक कुंजी का समर्थन करता है, उनमें से एक ऑटो-वृद्धि (आईआईआरसी, माईसाम टेबल करता है), तो आप कई उत्पाद विशेषताओं में "ऊपर" तालिका का पुनः उपयोग कर सकते हैं।


एक भी आसान कार्यान्वयन 20 डाउन "डाउन" में डालें और प्रति अनुरोध एक को हटा देगा। यह देखने के लिए कि उपयोगकर्ता सफल था या नहीं, प्रभावित पंक्तियों की संख्या (1 होना चाहिए) की जांच करें। यह मल्टी-उत्पाद स्पेशल के साथ अच्छी तरह से खेलता है।उदाहरण के लिये:

delete * from Down where down_special_id = Xxx limit 1 

शायद सबसे आसान, और यह भी, जो "जीता" का ट्रैक रखने के उत्पाद प्रति एक पंक्ति बना सकते हैं और प्रत्येक उपयोगकर्ता अद्यतन एक (और केवल एक) पंक्ति में है। फिर, यह देखने के लिए प्रभावित पंक्तियों की संख्या की जांच करें कि वे सफल थे या नहीं:

update Up 
set 
    user_name = @user_name 
where 
    user_name is null 
    and product_id = @product_id 
limit 1 
+0

माईएसक्यूएल सिंटैक्स क्षमा करें। मुझे लगता है कि SQL सर्वर और अन्य "टॉप" बनाम "सीमा"। – EthanB