Currently in the development of the system has a settlement logic, each order to got their sums period, the platform will be for the merchant to settle. Operations related to accounts include the deduction of the platform account balance and the increase in the merchant account balance, as well as the record of the account flow.
Like this scenario, if concurrency is not considered, then it is easy to have inconsistent data, leading to accounting confusion. Of course, this is more than (Xiāng) than (Dāng) deadly!
So how do we solve this concurrency?
For the sake of description, we simplify the scenario: there is an Account table in db that records the information of the platform and the merchant, and the program logic reads out the specified account record and modifies the value of one of its fields. In this scenario we see how concurrency is handled.
Presumably everyone can think of, with lock. Lock marks the statement block as a critical section, gets the mutex for the given object, and then executes the statement block, releasing the lock after execution completes. This can control the concurrency of in-process multithreading.
Here, I say another perhaps better way ———— with a timestamp. First look at the code logic:
Public voidMybiz (stringName ="") {Stopwatch Watch=NewStopwatch (); Watch. Start (); intLoops =0;//used to record the number of cycles inti =0; while(i = =0)//A successful update returns 1 when performing an update to the DB, otherwise 0 is returned (in non-exceptional cases). So, whenever we return to 0 o'clock, we try to execute the entire logic again.{Loops++; #regionBusiness logic//1. Read varDal =NewGateWay.DAL.PriceDal.PriceDAL (); stringMercode ="000001"; T_info_meraccount Accountmodel= Dal. Getacinfo (Mercode,"1","3"); if(Name = ="") {Accountmodel.mername= Accountmodel.mername +loops; } Else{accountmodel.mername=name; } //to simulate concurrency, let the thread randomly sleepThread.Sleep (NewRandom (). Next (Ten, +)); //2. Writei =Update (Accountmodel); #endregion if(i = =0) {Console.WriteLine (Thread.CurrentThread.Name+"encounter I=0, then try again ..."); }} watch. Stop (); Console.WriteLine (Thread.CurrentThread.Name+"number of executions:"+ Loops +"duation:"+Watch. elapsedmilliseconds);}Private Static intUpdate (T_info_meraccount model) {stringsql ="update t_info_meraccount Set [email protected],[email protected] where [email protected] and [email protected]"; inti =0; using(varconn =connutility.gatewayconntion) {conn. Open (); I= Conn. Execute (SQL,New{Name=model. Mername, Lasttime= Commondatatype_datetime.gettimestamp (false), Accode=model. Accode, LastTime1=model. Lasttime}); returni; }}
As you can see, the code logic is to take out a record first, then modify its Mername property value, and then persist the modification to db.
You can also see that the time stamp is used in this program. In table T_info_meraccount There is a timestamp field lasttime varchar (20). When you perform an update to a table, the WHERE clause appends a lasttime to the necessary accode conditions. As you can see, when the lasttime is changed by another thread, the update fails and returns 0. Then the while loop continues until it returns 1.
Is it reasonable? Let's write a testcase to simulate multi-threaded concurrent operations:
[TestMethod] Public voidtestconcurrency () {mybiz ("20161129 test Merchant"); Thread.Sleep ( +); List<Thread> ths =NewList<thread>(); for(inti =0; I <Ten; i++) { varThread =NewThread (() = { Try{mybiz (); } Catch(Exception ex) {Console.WriteLine (Thread.CurrentThread.Name+"--"+Ex. Message); } }); Thread. Name="Thread"+i; Ths. ADD (thread); } ths. ForEach (t=T.start ()); Thread.Sleep (Ten* +); //Thread.Sleep (1000); //Test ("20161129 testing Merchant");}
Test output:
> Executions: 1 duation:1127>thread1 executed: 1 duation:162>thread7 encounter i=0, then retry ... >thread8 encounter i=0, then try again ... >thread9 Encounter I=0, then try again ... >thread6 encounter i=0, then retry ... >thread2 encounter i=0, then retry ... >thread9 executed: 2 Duation:140>thread8 encounter i=0, then retry ... >thread6 encounter i=0, then retry ... >thread7 encounter i=0, then try again ... >thread2 encounter i=0, then retry ... >thread3 encounter i=0, then try again ... >thread3 Execution count: 2 duation:699>thread5 encounter i=0, then retry ... >thread7 encounter i=0, then retry ... >thread2 encounter i=0, then retry ... >thread6 encounters i=0, then retries ... >thread8 encounter i=0, then retry ... >thread0 encounter i=0, then try again ... >thread4 encounter i=0, then retry ... >thread5 performed: 2 duation:1146> Thread2 encounter i=0, then retry ... >thread7 encounter i=0, then try again ... >thread0 encounter i=0, then retry ... >thread4 encounter i=0, then retry ... >thread2 executed: 5 Duation:1398>thread7 encounter i=0, then retry ... >thread6 encounter i=0, then try again ... >thread8 encounter i=0, then retry ... >thread7 executed: 6 Duation:1630>thread0 encounter I=0, then try again ... >thread4 encounter i=0, then retry ... >thread0 performed: 4 duation:2081>thread4 encounter i=0, then retry ... >thread6 encounter i=0, then retry ... >thread8 encounter i=0, then retry ... >thread8 performed: 6 duation:2587>thRead6 encounter i=0, then retry ... >thread4 encounter i=0, then try again ... >thread6 performed: 7 duation:2825>thread4 executed: 6 duation:3328
The final value in DB is 20161129 test merchant 1222564676. Visible, verify that our code is OK.
This approach also effectively handles concurrency. So, compared to lock, where is its advantage? Lock can only control the same process thread. When the program is deployed on a different host, lock looks weak. The latter scenario solves the concurrency of multi-machine deployments.
Finally, a timestamp generation algorithm is attached:
/// <summary> ///gets the current time stamp/// </summary> /// <param name= "Bflag" >gets a 10-bit timestamp when true, and a 13-bit timestamp for false.</param> /// <returns></returns> Public Static stringGettimestamp (BOOLBflag =true) {TimeSpan TS= Datetime.utcnow-NewDateTime (1970,1,1,0,0,0,0); stringRET =string. Empty; if(bflag) RET=Convert.toint64 (TS. totalseconds). ToString (); Elseret=Convert.toint64 (TS. TotalMilliseconds). ToString (); returnret;}
When the accounting system encounters concurrency