بیشترلیست موضوعات • IndexWriting Mobile Code Essential Software Engineering for Building Mobile ApplicationsBy
Ivo Salmre Publisher: Addison Wesley ProfessionalPub Date: February 01, 2005ISBN: 0-321-26931-4Pages: 792
توضیحاتافزودن یادداشت جدید
Design Guidelines for Using Threads in Your Mobile Application
Have One Main Thread for Your User Interface
As noted earlier in the chapter, you should think of your main thread as acting like the front desk receptionist at a good hotel. The most important priority for the front desk is to be available and responsive to needs of customers. When asked to do something, the front desk person may perform the task himself if the task is quick. Longer-duration tasks are shuttled off to other hotel employees so that the front desk can remain responsive. A similar model should be applied to your application design with the front desk representing your application's user interface and other hotel employees representing background threads.
Design Your User Interface for Maximum Responsiveness
Foremost in your design goals should be to ensure that your application's user interface remains responsive and interactive with the user. This means avoiding long stalls in the user interface's operation as well as keeping the user informed and feeling part of background tasks whenever possible and appropriate.
Start Off with a Single-Threaded Application Design
Because additional threads greatly increase the complexity of your application, additional threads should only be added when a proven need arises. Avoid being "thread happy."
Consider Using a Wait Cursor Approach Instead of Multithreading for Simple Cases
Wait cursors are the poor man's version of multithreading, but the approach has much to recommend it, most of all simplicity. Instead of doing work in the background asynchronously, displaying a wait cursor is a polite way to tell users to wait because the application is doing work on their behalf.When you have identified locations where latency occurs, consider using a wait cursor to indicate to the user that work is being done and that they will be notified when their interaction with the application can resume. Your first line of defense in ensuring a good user interface is simply making the user aware when the interface is going to be unresponsive for a few moments. Displaying a wait cursor is a good way to inform users that work is being done on their behalf and that the work should be completed shortly.
Consider Using a Background Thread If the Latency Is Long or Indeterminate
A wait cursor approach is not appropriate if either (1) the length of the task to be performed is long enough to frustrate the user, or (2) the duration of the task is unknown or unbounded, such as when accessing an off device resource. In these two cases, consider a background thread approach.
Design Threading Code for Simplicity and Document for Safety
Thread safety is tricky stuff. Without careful attention to how member variables are being read and written, your application can end up reading a variable on one thread that is in the middle of being written by another thread; the "atomicity" of most in-memory data operations is not guaranteed because it takes several microprocessor instructions to write most data types. The fact that these issues are timing dependent and do not occur often makes them very difficult to track down, reproduce, and debug. Even when variable access can be guaranteed to be atomic, without attention to how a class' member functions are called you can end up in a situation where data gets corrupted or program logic behaves in an unexpected way because related data is modified concurrently by algorithms running on different threads; imagine two threads inserting and deleting to the same linked list at the same time. To deal with these situations robustly, it is necessary to define "critical sections" in your code; these ensure that only one stream of execution can concurrently run any code tied to the same semaphore object. (This is done in C# using the lock(object) statement and in Visual Basic using SyncLock(object)see the MSDN language reference for these two statements for more details.) To complicate matters further, code can also "deadlock" if two threads currently in different critical sections try to call code that needs to enter a critical section currently "owned" by the other thread; both threads' execution will halt at the entrance to the other's critical sections. For this reason, as well as for performance concerns, overly liberal use of critical sections can cause its own problems.You could try to make all your classes' properties and methods thread-safe; however, ensuring this would be exceedingly difficult and wasteful from a performance and design perspective. You would end up with many different critical sections throughout your application's code along a myriad of objects for the critical sections' use as semaphores. This kind of code is extremely difficult to design and test and can suffer unnecessary performance overhead due to thread-safety checks and unnecessarily serialized execution. Neither the .NET Framework nor the .NET Compact Framework attempts to do this; instead they both take a documentation approach and explicitly declare which operations are thread-safe and which are not. Developers are expected to read the documentation and use the classes, properties, and methods accordingly. A class with a method that is not thread-safe should not have that method called from different threads concurrently. Instead, either two different instances of the class should be created or the calling of the non-thread-safe method should be serialized by placing it into a critical section in your application's code. What is necessary and thread-safe is exposed that way, what is not is documented.You should take a similar approach in your own design. Make the surface area of classes, functions, and properties that need to be accessed by multiple threads as small as possible and state these explicitly in your code. Critical section locks should only be declared and used when multithreaded access is absolutely required and concurrent execution or data-access problems in your code are identified that cannot be easily fixed by improved design. Design, code review and test these special classes and functions rigorously and document your classes, properties, and methods accordingly. If accessing a type, property, or method is not thread-safe or you have doubts as to whether it can be concurrently accessed safely from different threads, document this in your code. For example:
// THIS <VARIABLE/PROPERTY/METHOD> IS NOT INTENDED TO BE // ACCESSED FROM MULTIPLE THREADS!!! // The expected use of this method is by the // <foreground/background> thread to...
Identifying what the critical parts of your code are that need to be thread-safe and explicitly stating which pieces are either not thread-safe or have not been designed with thread-safety in mind allows you to concentrate your design efforts on those pieces that truly need to be accessed concurrently by multiple threads. Explicit documentation will also help ensure that the code remains robust as it is evolved and maintained in the future.
Consider Cases Where Work Can Preemptively Be Done
Some user requests take a while to process. For example, a significant amount of data may have to be loaded or calculated, network requests may need to be made, or complex images may need to be rendered to respond to an upcoming user request. If this work stalls the application, the device's user will get frustrated waiting for it. It would be good to avoid this frustration when possible. If your application can with sufficient probability predict its user's next actions and has enough information to know what heavy processing or high-latency work will be required to react to them, it is worth considering preemptively doing this work on a background thread in anticipation of the user's next actions. By analogy, if you are running a restaurant and every morning the same person comes in at 8 a.m. and impatiently orders two fried eggs, a blueberry muffin, a bowl of cereal, and a cup of coffee, it is worth considering having these ready and waiting for the impatient customer. Even if one out of a hundred days the customer does not show up, it is still worth the effort if it greatly increases the customer's satisfaction on a majority of days he does show up. By having an on-demand service ready for demanding customers, you are providing a unique and valuable service for them.Preemptively doing work based on a request the user has not yet made is a good way to eliminate or significantly lower the length of annoying stalls a user will face when using your mobile application. When done well, this kind of approach can significantly raise users' perceptions of the quality of your mobile application, because it provides the instant-gratification experience that users seek when using mobile devices.As with the earlier warning about not becoming "thread happy," it is important to prove the need for preemptive processing in your application before embarking on designing these kinds of systems. Preemptive processing can add application complexity and should only be used if the payoff in user experience justifies it. The best way to prove this is by getting accurate time measurements for the delays users will face as well as testing out the predicted results of preemptive processing.
Predicting User Needs in Advance
Doing predictive work on behalf of the user can be tricky but can also be used to great effect. When it is done well, users almost never appreciate the hard work that is being done to meet their needs; things just seem to work smoothly.A good example is the picture viewer in Windows XP. This viewer pops up when you double-click a photo in a file-explorer window. The photo to be viewed is loaded and displayed. Unbeknownst to the user, the next photo in the directory gets loaded in the background while the first one is displayed. When the user does the most common next action (clicks the Next Image button in the photo viewer), the next photo will probably pop up without visible delay. This is no small feat for today's large digital photographs, which can take a significant amount of time to load, decompress, and size properly for display.