Vernacular concurrency conflicts and thread synchronization (1)

Source: Internet
Author: User
The monkey looked up and said, "I have a dream. I think when I fly, the sky will open the way. When I enter the sea, the water will also be divided into two sides. When I see the gods, I am also called a brother. It's carefree, there is no thing to hold me in the world, no one to worry about me, no more to where I can't, no more to what I can't do, no more to what I can win."
-- Why?
From Wukong Chuan
Summary

Do not enter male programmers. (Because female programmers may have less power to shoot bricks, so they can hold on ......)

Concurrency conflicts-what happens when a worm encounters two chicks?

What happens when a worm encounters two chicks?
Surely, the worm will surely see God.
What is uncertain is whether the worm first sees God or the next half?
You may be wondering, "I worked overtime until last night, and now I am still dizzy. If you want to visit the blog Park, you can temporarily forget the complicated and changing demands, the Bug that cannot be prevented, and the confusing office politics. Instead, you have encountered a mental illness and are wondering about this boring problem here ."
Believe me, this is definitely an important question of life! Think about it. If it was a bug, I 'd see God first. God would ask him, "What is your name? How did they die ?", The second half of the worm replied, "My name is bug a, which was eaten by chicken B ." Therefore, God wrote in his book: "worms are eaten by chicken B ." Then, after a while, the insects saw God in the first half. God also asked him, "What is your name? How did they die ?" The first half of the worm replied, "I'm called a worm and it was eaten by a chicken ." God opened his book and found that "worm a was eaten by chicken B .", However, at this time, the lower part of the worm had no idea where it was going to go, and he could not ask in detail. Therefore, God had to choose to trust the upper half of the worm, change the record on the book to "the worm is eaten by the chicken."
But if it happens to be the first God to see the worm, it will finally say "the worm is eaten by the chicken B" in the Book of God ".
That is to say, the death of this poor worm counts on the head of a chicken, which is completely unpredictable! This problem not only makes God quite a headache, but may also cause the programmer to lose his life. I want to know what will happen later. After an advertisement, I will be back soon!

Ad sound: "blog garden, not a cabbage garden, not a kindergarten, not an amusement park, not even luobjiayuan ...... Blog Park, an online home for programmers ."

1-2-3 super programs

I recently developed a very powerful program for the customer.

Class Program
{
Static int n = 0;
Static void foo1 ()
{
For (int I = 0; I <1000000000; I ++) // 1 billion
{
Int a = n;
N = a + 1;
}
Console. WriteLine ("foo1 () complete n = {0}", n );
}
Static void foo2 ()
{
For (int j = 0; j <1000000000; j ++) // 1 billion
{
Int a = n;
N = a + 1;
}
Console. WriteLine ("foo2 () complete n = {0}", n );
}
Static void Main (string [] args)
{
Foo1 ();
Foo2 ();
}
}

How is it? After 40 seconds, the program calculates the number of times for a variable n whose initial value is 0, and the value of Variable n is equal to 2 billion. What? You said I'm wasting my CPU resources and I'm not doing anything right? What do customers like! By the way, my computer was bought eight years ago, and I used a 800-core CPU.

Can it be faster?

However, the customer thought it was too slow and threatened to let me see God if it could not be compressed to less than 10 seconds.
Why are my customers so cruel? Alas, from the very beginning, I thought this old goat Hu, who always liked "hit the ground", was a little familiar. I regret it too late. Try using multiple threads.

Multithreading

Now I know why my program is implemented using two functions: "foo1 ()" and "foo2? This is because I have prepared to use multiple threads. Now I only need to use two threads to execute "foo1 ()" and "foo2. (How can a good architect be inferior to a fortune teller ?)

Class Program
{
Static int n = 0;
Static void foo1 ()
{
For (int I = 0; I <1000000000; I ++) // 1 billion
{
Int a = n;
N = a + 1;
}
Console. WriteLine ("foo1 () complete n = {0}", n );
}
Static void foo2 ()
{
For (int j = 0; j <1000000000; j ++) // 1 billion
{
Int a = n;
N = a + 1;
}
Console. WriteLine ("foo2 () complete n = {0}", n );
}
Static void Main (string [] args)
{
New Thread (foo1). Start ();
New Thread (foo2). Start ();
}
}

Aren't you surprised? All of the results are incorrect, and the results of each execution are different! Before blaming CPU bugs, memory faults, and viruses in the operating system, let's take a look at how the code is executed.
Note: After multithreading is used, the execution time of the program is 34 seconds, so even if the results are correct, my life is not guaranteed.

Add n to 1 in three steps

In the above specially designed example, n plus 1 is divided into three steps:
Step 1: Save the n value to.
Step 2: calculate the value of a + 1.
Step 3: Save the result of a + 1 to n.
If you are not sure whether the preceding three steps are true, you can check the assembly code of the program (the method is to execute one step in VS2005 first, then use the menu "Debug> WINDOW> disassembly Ctrl + Alt + D" to open the Disassembly window ). The assembly code is intercepted and the corresponding pseudo code is used. Different Background colors are applied for later use.

As we know, there is only one CPU, so the so-called "concurrent execution" of multiple threads is just to queue these threads, and then let them take turns to use the CPU one by one, each thread only uses a very short time. In this way, it seems that there are multiple threads executing at the same time for humans to be unresponsive. Let's take a look. When the first thread finishes executing "Save n value to a", the time is up! This is the turn of other threads to execute. What will happen at this time? At this time, you will hear a big drink in Windows: "help me take care of my grandfather seven and his three sisters --", then click it to pause the first thread. Therefore, it is not surprising that the first three cycles of the first and second threads are executed in the order shown. (The yellow background code belongs to the first thread, and the green background code belongs to the second thread)

Now, the loops in the first and second threads are respectively executed three times, and the value of n is 3, rather than the expected 6. Therefore, even if our computer only has one CPU (not dual-core ),Concurrency conflict.

Is there anyone in Windows?

I heard that a thread has a "Conflict" and we have all opened our eyes wide, but in fact there is no excitement-the two threads are not open. Although they access the same global variable n, they do not do any harm to the other party or themselves. We say that the two threads have a concurrency conflict. What we want to express is simply "they have done the repetitive work we don't want to see.

Well, to survive, we must find a way to prevent the two threads from doing repetitive work-that is, thread synchronization. But before that, let's take a look at under what circumstances there is no need to worry about thread synchronization.

Thread Synchronization is not required.

1. You can use an Assembly statement to read and assign values to n.

Let's make a slight change to the program:

As you can see, "n = n + 1" corresponds to only one line of assembly code "inc dword ptr ds: [01608A60h]", that is, the n value in the CPU cache is directly increased by 1 through the inc command. The catch of CPU is a convenient invention. However, it is still a bit early, because we have not considered the situation of multi-CPU. You need to know that most of the current servers have more than two CPUs, and even the PC is dual-core (one chip contains two logical CPUs and two caches, it's no different from installing two CPUs. Since the CPU does not immediately write n values to the memory after increasing n in the cache by 1, so if we execute the above program on a computer with two CPUs installed, and assume that the first thread is executed by CPU1, and the second thread is executed by CPU2, the first three cycles of the two threads are likely to look like the following:

Unfortunately, the value of n is 3 rather than the expected 6. The convenient invention of CPU cache is now a hot potato. However, if you run the above Code on a computer with Intel's dual-core CPU, you will find that the result is still very correct. It seems that the troubles have not occurred. Why? This is because the CPU Of The x86 architecture is very authentic and it has made a lot of efforts to ensure cache consistency.
===== Update ====
Today, I tested the above Code on Intel's dual-core CPU and found that no matter whether or not the volatile keyword is added, the result is incorrect !!! I don't know if my understanding of volatile is incorrect. The following Interlocked. Increment () result is correct. I am very sorry for not carefully testing my code!
======================================
The bad news is that not all CPUs are as authentic as x86 CPUs (for example, IA64, because of performance and other considerations, it will not make more efforts in cache consistency). The good news is: non-authentic CPUs such as IA64 provide volatile read (force read from memory) and volatile write (Force write to memory) operations. Correspondingly,. net provides the volatile keyword. Therefore, we only need to add the volatile keyword when defining n.

Note: No matter the volatile keyword is added to my 800 CPU, the program execution time is about 14.5 seconds.
You may not like to use the volatile keyword when declaring variables, because the memory will be forcibly refreshed whether multithreading is used or not, whether reading or writing n; and if you pass n to the method by reference, for example, write int. tryParse ("123", out n), the volatile keyword is invalid. So. net provides another solution: You can use Thread when reading and writing n, instead of using the volatile keyword when declaring a variable. volatileRead (...) and Thread. volatileWrite (...) these two static methods. In addition, the Thread. MemoryBarrier () function saves the data in the cache to the memory.

You can also use more advanced mutual lock methods, such as Interlocked. Increment (). When the thread calls the Interlocked method in the Interlocked class, the CPU enforces cache consistency. In fact, all thread synchronization locks (including Monitor, ReaderWriterLock, Mutex, Semaphore, AutoResetEvent, and ManualResetEvent) Call the interlock method internally.

This program runs for 60 seconds on my server 800 CPU.

2. You add m, I add n, and each

Eliminating shared resources between threads is undoubtedly a base-paid method.

Although this method is cool, it is not very practical. It is difficult for me to prevent other programmers from using two threads to execute foo1 ().

3. Only one thread assigns a value to n. Other threads only read n values and do not care if n values are the latest.

For example, in the following program, foo1 () is responsible for accumulating n values, foo2 () is responsible for reading the progress and displaying the progress to the user. Users, looking at the progress is just to confirm that foo1 () is indeed working hard, rather than stocks, chatting with QQ, posting bad websites or writing blogs.

This article ends here, Sleep (10 million milliseconds) first. Next, we will introduce the thread synchronization method.

Note: Two chicks are raised in the House, and they are placed on the windowsill to bask in the sun during the day. Sometimes, when they find something delicious, you will catch up with me, from one end of the window sill to the other, and then two small wings, brake, and then round the small claws quickly ran back, it seems very interesting to have a sports meeting.
Looking at the two pale yellow velvet balls on the window sill, I suddenly felt that life was so beautiful and at the same time so fragile and small.
Good luck and strength for all people in the earthquake zone!

References

Jeffrey Richter,CLR via C #, Second Edition. Microsoft Press 2006.
Translated by Thomas et al and Sun Yong,Chinese version of Programming Ruby. Electronic Industry Press, 2007.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.