OO second blog assignment

OO second blog assignment

write in front

  After three weeks of multi-threaded work, I am fortunate enough to live on the edge of sudden death every day, so let's celebrate here first. Although the process was painful, I benefited a lot from these three multithreaded programming assignments. Some of these contradictions have impressed me, and I summarize these contradictions in the following 3 points:

  1. The contradiction between the randomness of multithreading and the accuracy of the results
  2. The contradiction between multithreading concurrency and thread safety
  3. Conflict between the number of threads and program performance

The specific manifestations of these contradictions will be described in detail in the following job analysis process.

Multithreaded elevator

1. Design strategy

  The first multi-threading job was during the Qingming holiday, and I had plenty of time. I spent two days doing a lot of preparatory work, including learning about java multi-threading programming and the pre-planning of the job. I wrote the first two times. The bloated and ugly elevator has been refactored, retaining the original core scheduling strategy (double queue: request queue, waiting queue; main request; button), almost rewriting the entire code, although it violates the job requirement to inherit the previous intent . But I think this refactoring is worthwhile and quite productive.
  This job uses the classic producer-consumer model and uses blocking queues to ensure the security of shared data. I will not repeat the common parts. The following are the scheduler scheduling strategy, elevator operation strategy, and scheduler and Synergy between elevators.

  The program is mainly composed of three classes: request generator, scheduler, and elevator. The three classes transmit information through Request object, blocking queue and button switch.
  The request generator is responsible for generating the request according to the console input, judging the validity of the request format, and adding the request to the blocking queue shared with the scheduler.
  The scheduler is responsible for allocating requests, and there are two queues in it, one is the request queue shared with the request generator, and the other is the waiting queue. The specific scheduling strategy is as described in the preceding flow chart.
  The elevator is responsible for running, and adjusts its state according to the current main command and the commands in the piggyback queue.
  Since the scheduler needs to obtain the state of the elevator for judgment, in order to avoid thread safety problems, some state get methods are synchronized with synchronized, and some states directly use atomic classes and atomic operations.

2. Program structure analysis




I am quite satisfied with the structure of this job. Since some functions of the original scheduler have been moved to the inside of the elevator, the elevator class seems to be a bit huge, and there is a lot of data shared between the elevator class and the scheduler, so a lot of synchronization is used to avoid threads safe question.

3. Bug analysis

  Due to the excellent preparation and solid preliminary planning for this assignment, I encountered almost no functional bugs during my own debugging process, and there were no bugs in both the public beta and mutual beta testing. However, there is a very obvious contradiction, which is the contradiction between the randomness of the first multi-threading and the accuracy of the results . Due to the uncertainty of the input time of the request, the operation of the elevator actually has a considerable degree of randomness. However, the test requires accurate results, and the contradiction between the two is almost irreconcilable. There are two main conflicts:

  1. Use fake time or system time
  2. Handling of time boundaries

  Since it takes time for the program to run itself, if only outputting the system time will result in accumulated errors as the program runs for a long time, many students have turned to the fake time camp after realizing this problem (the fake time is when the information is output). It uses an absolutely accurate time calculated through a series of processes from the start time), and later derived the "true and false time" camp that uses the theoretical sleep time minus the actual running time to get the actual sleep time.
  The time boundary problem was a big worry for me in the early stage. For example, if three elevators theoretically arrive at the 2nd floor at the same time, but the amount of exercise varies, there is a 10th floor request that has not been processed before arriving. According to the scheduling principle It should be assigned to the elevator with the least amount of movement, but in fact these three elevators do not really arrive at the same time, there will always be a few milliseconds error, which will cause this request to be assigned to the first elevator to reach the 2nd floor, Not the elevator with the least amount of exercise. My solution to this problem is: after the elevator reaches a certain floor, it sends an allocation request signal to the scheduler, and the scheduler sleeps for 20ms before starting the allocation after receiving the signal. This 20ms can ensure that all three elevators have reached the 2nd floor. Thus solving the problem. Of course, this is a relatively bad solution, but due to lack of experience at the time, even such a bad solution was a rare feasible solution after a lot of discussions with students. After the taxi operation, I have a new solution - setting the minimum time granularity, this method can solve the problem of time boundary well, this solution will be introduced in detail in the taxi operation.
  The code I got during the mutual testing stage has serious problems with the handling of the amount of exercise, and there were problems in the public beta, but the basic functions are all normal, but the structure and naming conventions of the code are not well done, and there is UML cooperation The suspicion of the spaghetti code is about 400 lines in a single scheduler, and the readability is extremely poor, so I gave up the process of reading the code and directly combined the readme test. After a lot of bombardment, basically all the errors are related to the amount of exercise, and the public beta of this point has been suspended, so I have to use the killer, that is, the multiple elevators mentioned in the previous paragraph are running, and multiple elevators stop at the same time , the request allocation problem at the stop time. Sure enough, it was assigned to the first elevator to arrive, and there was a bug.
  Since this assignment is the first time I have been exposed to multithreading, I haven’t talked about thread safety issues, and I haven’t considered how to test thread safety issues. I simply use the console to control the time for input. In retrospect, I think it was true at that time. So primitive, how easy it is to write a test class.

4. Experience

  After this assignment, I have a preliminary understanding of multi-threaded programming and a certain understanding of the producer-consumer model. I personally think that the code of this assignment is the best one written from the beginning to the time, no matter in terms of the responsibilities of each class, the synergy between the classes, the responsibilities of each method, and the total amount of code. All are better.

IFTTT

1. Design strategy

  This assignment is the most pitiful assignment so far. Why use "pit"? The main reason is that in terms of difficulty, this assignment is actually not significantly improved from the previous assignment, but this time the instruction book is simply a book from heaven , there are too many problems in it that are not explained clearly. This directly caused me to not figure out what I was going to write about this assignment by Sunday night. In desperation, I had to combine the instructions and various or even contradictory answers in the issue and customer service groups, using " Occam's Razor " (if not necessary, do not add entities, that is, simple and effective principles), made a In conclusion, the triggering conditions of various triggers and the monitoring of the catalog are based on the instruction book, and all the plausible parts that are not explained are "shaved", which greatly simplifies the difficulty of the work, and proposes a simplified version of the instruction book. . What makes me gratified is that my understanding has been approved by the teaching assistant and supported by many classmates, so that the issue#32 I released appeared in the readme of many classmates. I got this in the mutual testing stage. Homework is just that.
  This job opens up a new idea, which is state snapshots. Record the status information of all files in the monitoring directory at a certain moment through snapshots, take snapshots regularly and compare two adjacent snapshots to find changes. Since each field of file information in the snapshot is generated at the moment when the snapshot is generated It has been determined that it will not change, so there is no thread safety problem when the monitor accesses the snapshot.
  Since this job actually started on Monday, the time is very tight, there is almost no pre-planning, and the algorithm is poorly written. One trigger on each path corresponds to one thread, and the top-level directory of each trigger has a All snapshots are managed by a HashTable, and the key value is the directory file itself or the upper directory of the ordinary file, which reduces some duplicate snapshots, but still has a lot of redundancy. All triggers wake up every 1s. After waking up, save the previous snapshot and then refresh the snapshot. Compare the new snapshot with the previously saved snapshot to find the difference to see if the trigger conditions are met. If the corresponding tasks are met, in order to prevent multiple triggers The simultaneous execution of tasks by the processor causes some inexplicable and wonderful things to happen (the recovery task is the most obvious), and all trigger threads have to be mutually exclusive. On the other hand, in order to avoid security problems in the file class, a static lock is set for all file operations. This ensures that only one thread can operate on the file at the same time, so as to achieve thread safety, which leads to the second contradiction mentioned above: the contradiction between multi-thread concurrency and thread safety . This processing method is obviously at the cost of sacrificing concurrency. Since static locks are added to all parts except sleep in the run method of the trigger class, in fact, all the triggers in the file state management part add up to one thread, and then With one thread for the test class and one thread for the main, there are only three threads in the whole, and the concurrency of multi-threading is not well utilized.

2. Program structure




This time the work pit is in the instruction book, the program itself is not too difficult, and the structure is not too complicated.

3. Bug analysis

  The subjective factor of bugs in this homework is really too large. Many bugs are not caused by my own program writing, but by changing requirements.
  Due to the processing method I described above, at the expense of concurrency in exchange for thread safety, basically there will be no problems with thread safety (at least after my own extensive testing), but it is obviously insufficient in efficiency and processing speed. It is slow, and it will not respond when there are too many monitored files. You can only use the readme method to force a period of sleep between the two tests. This period of time is adjusted according to the number of triggers and monitoring files, at least sleep(1000), Actually I didn't solve this problem myself. Fortunately, the classmates who gave me the test obeyed this rule, so I didn't get caught with a bug in this assignment.
  The job I tested had a lot of problems during the public beta phase, mainly because it didn't respond to various changes to the file at all. Later, I read the code and found that this was caused by the same problem. Since the test thread was started without any rest processing after all trigger threads were started, the test thread had already modified the file before the trigger had time to create the initial snapshot. , so the corresponding changes cannot be captured, which is regarded as a bug. On the other hand, the program's understanding of the guide is quite different in some places. For example, the summary output is the total number of triggers fired, and only 10 triggers can be created instead of 10 monitoring paths. The assignment I got requires at least 3s sleep between two requests (more than my requirement...), so it is actually difficult to find thread safety related issues, but it seems that most students are Using this method to avoid thread safety problems, the final result is actually the same as my sacrifice of concurrency for thread safety, and even the efficiency is not as high as mine. (ps. Extending the file operation time by writing very large data causes thread safety issues, I don’t think it’s very ethical, and I’m useless, but adding static locks through file operations can really solve this problem)

4. Experience

  How to maintain concurrency while ensuring thread safety is the one question that made me think the most about this assignment. Later, I thought maybe a little crazy, I felt that security was the priority, and the accuracy of the results of public testing and mutual testing was extremely high, and errors caused by randomness were not tolerated. Concurrency relegated to a very backward position, plus With the assist of a certain dalao, the next job will lead to a point of no return...

taxi

1. Design strategy

  My initial idea was to open 100 threads for 100 taxis, the scheduler to open one thread, and the input to open one thread. The basic mode is similar to that of a multi-threaded elevator, except that the one-dimensional motion is changed to two-dimensional motion. I think there is a problem. In the past, the elevator opened 3 threads and accumulated errors for a long time. This time, with 100 taxis, the error is even more difficult to avoid. This leads to the third contradiction mentioned above, the number of threads and program performance contradiction . I tried the effect of opening 100 threads in the third OO on-board experiment, most of the time was spent on context switching, the running speed was no faster than opening 5 threads, and this time the job taxi ran for one grid The time is only 200ms, the performance requirements are extremely high, and no stuttering is allowed. The status of 100 taxis must be adjusted within 200ms, and the corresponding requests must be allocated. It seems too much to open 100 taxi threads. More, and some impractical.
So there is a saying that there is a road of no return. It is said to be the road of no return, but I think it may be a road to the sky after walking. This program comes from a dalao. Let’s go back and analyze the two problems in the previous multi-threaded elevator operation, one is false time and the other is time boundary. These two problems are almost unsolvable by relying only on multi-threading, and errors will inevitably occur. Caused by the randomness of multithreading itself. And the test does not allow the occurrence of such random errors (this is also a defect in the course requirements, I hope that it can be improved in the public testing and mutual testing stages in the future). .
  Analyzing this guide, the minimum time unit is 100ms, and it takes 200ms for a taxi to run one grid. 100ms is mainly to distinguish requests. The most critical fact is that in this 200ms, taxis start to run from 0ms at the beginning, and the time when all taxis are at a certain point on the map must be an integer multiple of 200ms. Since each request actually ends up being executed by the taxi, the actual time for the start and end of the request must be an integer multiple of 200ms. So you only need to scan the taxi list every 200ms, scan the request queue, and make sure that the status of all taxis has been determined when scanning the request queue, so there is the following method.
  100 taxis as a whole as a thread, the scheduler as a thread, scan these 100 taxis every 200ms, and call the function of each taxi to change its own state and location, wake up the scheduler after the state of all taxis has changed , to allocate the request. At this time, the status of all taxis has been determined, and there is absolutely no possibility of opening a hundred threads. When the allocation request is made, some taxis have completed the movement, and the other part of the taxis have not been completed. A mobile situation occurs. The two threads of the scheduler and the taxi list are mutually exclusive to ensure that the two threads operate alternately, and the sleep time is adjusted to 200ms minus the actual running time, so as to ensure that there will be no cumulative error.
  In order to further shorten the running time, I hang all the taxis on the map points corresponding to their positions. When the request arrives, the scheduler only needs to search for 25 points centered on the starting point of the request, eliminating the need to traverse 100 taxis and The hassle of making comparisons. On the other hand, I calculate the shortest distance between any two points on the map in advance during initialization, and then use it directly. After this series of processing, even if 300 requests arrive at the same time, I can complete a series of operations of traversing taxis and allocating these 300 requests within 50ms on my low-voltage dual-core i7. The performance is quite strong. The mutual exclusion processing security of the two threads is also guaranteed.

2. Program structure




Because the system itself is very large and complex, and the provided gui package has a chaotic structure, the whole measurement result does not look very good, and the class diagram is also very large.

3. Bug analysis

  Due to the use of the above structure in this assignment, a lot of problems have been avoided, and many troublesome problems encountered by other students have been solved by me. At the scale of concurrent requests, all taxi statuses are determined without inexplicable problems. From the completion of writing to the final submission, the output format and the adjustment time of some states have been changed according to a few details of the instruction book, and the modification process is quite simple, which made me realize that a good design can greatly simplify the later stage. Maintenance pressure, and no bugs were found in the mutual testing phase.
  The code I got during the mutual testing has several obvious problems. The first is the naming convention. Although most of the names are in English, there are some spelling errors in them, which hinder understanding. On the other hand, many variables declared in functions are only declared but not used, and there is often a continue statement that has no effect without a loop block. It is really difficult to read this code, but because I have to write the test code myself, I still read it patiently. During the process of reading the code, I found some problems. The taxi sleeps (200) for each movement step. The former makes it impossible to handle multiple requests arriving at the same time, and the latter causes accumulated errors. Both of these problems can actually be avoided with simple modifications. However, since only one request is allocated every 100ms, the operating pressure of the system is greatly reduced, many other problems are covered up, and no other bugs are found.

4. Experience

  In this homework, thanks to the help of dalao in the early stage, I put a lot of effort into the design mode, and I have a spectrum in my heart when I do the pre-design and officially start writing code in the later stage, which is much calmer than IFTTT. I also have a deeper understanding of the design patterns taught in the class. Opening a thread for 100 taxis really opened up a new idea for me.

Summarize

  These three weeks can be said to be the most painful three weeks for me in the past 20 years. I have never been so busy in my third year of high school. Under the double oppression of OO and OS and the interference of a bunch of chores, almost every night I go to bed at one o'clock in the morning. After half a day, Monday and Tuesday are basically after 2:30 in the morning, there are no weekends, not even the Qingming holiday, so I was in a state of high tension for three weeks, there was hardly any rest in between, and I often tried on the edge of sudden death. It can be said that it is also a kind of tempering of one's own will. The main reason why I was able to get through all the difficulties was actually the help of so many dalao. From the understanding of the instruction book, to the debugging of the bug, to the design of the test data, in the process of discussion, through the efforts of everyone to find the problem, analyze the problem and solve the problem, all kinds of novel ideas are weighed and compared with each other, and the best is selected. In the end, it will not be one by one @ everyone dalao, I would like to express my heartfelt thanks here.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325067196&siteId=291194637