Concurrent and Networked Systems: Patterns and Paradigms
110 likes | 121 Views
Explore the importance of concurrency and patterns in designing and implementing concurrent and networked systems. Learn about different paradigms and patterns used to manage complexity and improve performance.
Concurrent and Networked Systems: Patterns and Paradigms
E N D
Presentation Transcript
E81 CSE 532S: Advanced Multi-Paradigm Software Development Introduction to Concurrent and Networked Objects Chris Gill Department of Computer Science Washington University, St. Louis cdgill@cs.wustl.edu
Why Study Concurrency and Patterns? • Concurrent/networked systems increasingly important • Significant focus on (multicore) hardware, location independence, scalability, performance, timing etc. • Concurrent/networked Systems are highly challenging to design and implement well • Accidental complexities must be constrained • Limitations with tools, techniques and processes • Reinvention and rediscovery (NIH, “C++ considered harmful”) • Inherent complexities must be accommodated • Network partitions, partial failures, deadlock, race conditions • Patterns and pattern languages help to codify what works • Generative techniques adaptable to new contexts
Categories of Patterns • Service Access and Configuration • Appropriate programming interfaces/abstractions • Concurrency • Exploiting physical and logical parallelism • Synchronization • Managing safety and liveness in concurrent systems • Event Handling • Inescapable in networked systems
Aggregating Paradigms threads, queues, timers OS services OS OS • Single Paradigms • Procedural • Object-Oriented • Generics • Aspect-Oriented • Idioms, Design and Strategic Patterns • A “Multi-Paradigm” Paradigm • i.e., a Multi-Paradigm • i.e., Generative Programming static portability scheduling strategies composition end-to-end aspects OS OS
Procedural Systems Programming ACE_INLINE ACE_thread_t ACE_OS::thr_self (void) { // ACE_OS_TRACE ("ACE_OS::thr_self"); #if defined (ACE_HAS_THREADS) # if defined (ACE_HAS_PTHREADS) // Note, don't use "::" here since the // following call is often a macro. ACE_OSCALL_RETURN (pthread_self (), int, -1); # elif defined (ACE_HAS_STHREADS) ACE_OSCALL_RETURN (::thr_self (), int, -1); # elif defined (ACE_HAS_WTHREADS) return ::GetCurrentThreadId (); # elif defined (ACE_PSOS) // there does not appear to be a way to get // a task's name other than at creation return 0; # elif defined (VXWORKS) return ::taskName (::taskIdSelf ()); # endif /* ACE_HAS_STHREADS */ #else return 1; // Might as well make it the first thread ;-) #endif /* ACE_HAS_THREADS */ } • Common-off-the-shelf (COTS) Operating Systems • E.g., Linux, Windows, VxWorks, LynxOS, … • Standardized APIs • Threads, sockets, … • POSIX, etc. • Portability layer is useful • In C/C++, pre-compilers can do a lot for you From http://www.cs.wustl.edu/~schmidt/ACE_wrappers
Procedural Programming, Continued size_t i; for (i = 0; i < n; i++) { ACE_thread_t t_id; ACE_hthread_t t_handle; int result = ACE_OS:: thr_create (func, arg, flags, &t_id, &t_handle, priority, stack == 0 ? 0 : stack[i], stack_size == 0 ? 0 : stack_size[i], thread_adapter); if (result == 0) { if (thread_ids != 0) thread_ids[i] = t_id; if (thread_handles != 0) thread_handles[i] = t_handle; } else // Bail out if error occurs. break; } return i; • Inherent complexity • Part of the problem itself • Won’t go away • E.g., network latency • Accidental Complexity • Artifact of the solution • Can reduce with better tools and techniques • How many times would you want to (re-)write this code? See also http://www.cs.wustl.edu/~schmidt/PDF/IPC_SAP-92.pdf
Object-Oriented Programming ACE_IPC_SAP • Reduction in Accidental Complexity • Less tedious and error-prone to develop, port, maintain, extend • Inheritance Polymorphism • Allows substitution • Encapsulation • Hides details • Controlled violations may help • E.g., socket handle ACE_SOCK ACE_SPIPE ACE_SOCK_Stream ACE_SOCK_Dgram
Object-Oriented Programming, Continued class Synchronized_Processor { public: int critical_section (); private: Processor p_; Mutex m_; } int Synchronized_Processor:: critical_section () { m_.lock (); int result = p_.do_processing (); m_.unlock (); return result; } • The OO paradigm helps • But, leaves key questions open • When should we create objects? • What are their relationships? • How do they interact? • We still need tactical guidance
Design Patterns Container Iterator * make_iterator()=0; • Each pattern has a name • Part of a higher-level design vocabulary • It applies to a context • It documents a solution • It captures experience • We also need key idioms • E.g., guard, copy-on-write, etc. Array Linked_List Iterator * make_iterator(); Iterator * make_iterator(); template <typename T> class Guard { public: Guard (T & t); ~Guard (); private: T & t_; }; Application of the GoF Factory Method Pattern
Towards Strategic Patterns Sockets? Pipes? Event Multiplexing? Reuse? Portability? • Procedures + OO + Patterns • We’ve made progress on accidental complexity • But still have more to do • And, solutions must respect inherent complexity • Resource access • Communication • Asynchrony • Need strategic guidance to demystify the art
Strategic Patterns Active Objects • Strategic Patterns • Service Access & Configuration • Synchronization • Concurrency • Events • Small pattern-languages • E.g., ACT, Active Object • E.g., Acceptor, Connector, Reactor posts to queues director reactor thread