330 likes | 457 Vues
This guide explores key concepts in C# related to finalization, IDisposable, and threading. It delves into the functioning of destructors, the importance of the Dispose pattern for unmanaged resources, and the implications of garbage collection (GC). The section on threading discusses the creation and synchronization of threads, emphasizing critical sections and the use of locks for safe execution in concurrent environments. The guide also highlights best practices and potential pitfalls, ensuring developers can write efficient and safe multithreaded applications.
E N D
Threading and P/Invoke Tom Roeder CS215 2006fa
Finalization • Recall C++ destructors:~MyClass() { // cleanup} • called when object is deleted • does cleanup for this object • Don’t do this in C# (or Java) • similar construct exists • but only called on GC • no guarantees when
Finalization • More common idiom:public void Finalize() { base.Finalize(); Dispose(false);} • maybe needed for unmanaged resources • slows down GC significantly • Finalization in GC: • when object with Finalize method created • add to Finalization Queue • when about to be GC’ed, add to Freachable Queue
Finalization images from MSDN Nov 2000
IDisposable and using • Idea for common cleanupusing(T t = new T()) { // do work with t} // t.Dispose is called by runtime • IDispose provides one method: void Dispose() • must provide finalizer, since must be called • when called from finalizer, don’t undo managed obj • often add private void Dispose(bool) • using calls Dispose automatically • used in class libraries, eg. for Socket
Weak References • Sometimes want to keep references but not cause the GC to wait • MyClass c = new MyClass();WeakReference wr = new WeakReference(c); • Now c can be collected • wr will return null if referenced after c collected • but to truly access the object, we get a strong ref • this is the Target property • Why? • Useful for large objects • infrequently accessed • nice to have but can be regenerated
Resurrection • Imagine assigning this to global in finalizer • object is now reachable again! • called “resurrection” • if finalizer already called, access unpredictable • Should not do this • but, if do, may want to reject accesses in methods • what would this finalize code do:someObj = this;System.GC.ReRegisterForFinalize()
System.GC • Can control the behavior of GC • not recommend in general • sometimes useful to give hints • Some methods: • Add/RemoveMemoryPressure • ReRegisterFor/SuppressFinalize • WaitForPendingFinalizers • Collect
Memory Profiles • Good • lots of small short-lived objects • or few but long-lived ones • few links between old and new objects • Bad • frequently create long-lived objects • create many objects that live a long but fixed amount of time
unsafe mode • Sometimes need access to pointers • use unsafe modifier:unsafe public void Method(out int* pi) { int i = 10; fixed(int* pj = &i) { pi = pj; }} • what is wrong with this method? • unsafe modifier works a method modifier • or as a keyword for blocks
unsafe mode - Pointer details • Can only refer to “unmanaged” types • or enums • or structs composed of unmanaged types • Not a subclass of object • void* exists, but no arithmetic allowed • boxing/unboxing does not work • stackalloc gets memory from the stack
unsafe mode • Cannot be executed in untrusted context • security requirement: so can’t change memory • eg. avoid stack smashing attacks • pointer types • cannot refer to a reference • cannot refer to a struct that contains references • int* pi, pj; // NOT int *pi, *pj;
Threading • Threading provides concurrent execution • Even useful on multiprocessor • As opposed to “sequential” execution • Comes at cost of overhead • Can be dangerous • For examplepublic int Increment(ref int x) { return ++x; } • What happens when called by two threads? • How to fix?
Threading • How to create a Thread • Create a new Thread instance with delegate • Type: public delegate void ThreadStart(); • Call Start method • Let’s do an example on the board • Thread will be scheduled when possible • on SMP, may actually have many threads at once • on UP, still may be useful when blocked • as in UI, networking code, dealing with drivers
Threading • Need synchronization primitives • Way to ensure that only one thread executes code in a region at once • Called “critical section” • C# provides (mostly in System.Threading) • lock statement • Monitor class • Interrupt • several others (see Birrell’s paper or MSDN)
Threading model: lock • Basic idea: each object has a lockpublic int Increment(ref int x) {lock(this) return ++x;} • lock prevents more than one thread from entering • forces sequential order • What should we lock on? • for instance variables: this • for globals and statics: typeof(container) • something that will be same for all threads that access this shared memory
Threading model: Monitor • Monitors provide synchronization construct • entry waits on a queue • waiting lets a new thread enter • Monitor.Enter and Monitor.Exit • same semantics as the lock construct • Why do we need both? • Gets a lock on the object • Cannot be used on value types: why not?
Threading model: Monitor • Methods • Monitor.Enter/Monitor.Exit • enter/exit the monitor for a given object • Monitor.Wait • wait on a given object • must be inside a monitor for that object • signal-delayed semantics • Monitor.Pulse/Monitor.PulseAll • some thread(s) can be released to try the monitor
Threading model: Interrupt • Sometimes need to wake up a thread • eg. if UI cancelled • eg. if event no longer needed • Standard OO way: exceptions • Interrupt causes thread to throw ThreadInterruptedException • only on Wait or Sleep • Allows cleanup of invariants
Other synchro classes • Abort • throws exception immediately • difficult to clean up: Why? • usually too draconian • Mutex and other synchronization • good for interacting with Windows • but stick with lock and Monitor, normally • ReaderWriter Locks • not clear exactly what semantics they implement
Threading model: ThreadPool • Instead of explicitly creating threads • create a pool • pass delegates to worker threads • enqueued in pool • QueueUserWorkItem • takes a WaitCallback delegate • Good for large amounts of parallel work • eg. N-Body problem • automatically scales to number of processors • “embarrasingly parallel” problems
Threading Conclusions • Standard monitor semantics • as in Java • useful constructions • OS synchronization exposed • native ThreadPool • Really only need • lock • Monitor
P/Invoke • Use special attributes to make system calls • eg.[DllImport(“kernel32”)]static extern int GetProcessHeap(); • calls to function in C library • problems for C++ • name mangling: doesn’t match • General problem of COM/.NET interop • why does this matter?
COM Interoperability • Need metadata • generated from TypeLib • Can then use COM class like .NET class • even though major differences in models • eg. threading, registration, inheritance • useful for backwards compatibility • Can bind early or late to class • either we know its type at compile time or not
Calling a COM Server namespace CallingCOMServer { using System; using COMServerLib; public class DotNET_COMClient {... public static int Main(string[] args) { MyCOMServer myS = new MyCOMServer(); return (myS.Add (17,4)); } } };
Late-Bound Activation • Use Reflection to get the type of the class • ProgID / ClassID: looked up in registry • gives type of COM object • Can instantiate instance and call methods • uses InvokeMethod to call methods • don’t have as strong type information here • COM object wrapped by __ComObject • can sometimes use as to get better info
Late-Bound namespace LateBoundClient { using System.Reflection; ... Type typ; Object obj; Object[] prms = new Object[2]; int r; typ = Type.GetTypeFromProgID(„MyLib.MyServer"); obj = Activator.CreateInstance(typ); prms[0] = 10; prms[1] = 150; r = (int)typ.InvokeMember(„aMethod", BindingFlags.InvokeMethod, null, obj, prms); ... }
.NET from COM • Needs to be public with public methods • Need to register a wrapper: use RegAsm tool • provides ProgID/ClassID information for registry • necessary for COM integration • Metadata must be converted to TypeLibrary • Have same early- and late-bound activation • early: by name • late: by ProgID/ClassID
Platform from .NET • Calling static functions in DLLs • P/Invoke provides services • Locates implementing DLL • Loads DLL • Finds function address • Fuzzy name matching algorithm • Pushes arguments on stack • Performs marshalling • Enables pre-emptive garbage collection • Transfers control to unmanaged code
P/Invoke Example namespace HelloWorld{ using System; class MyClass { [dllimport(“user32.dll”, CharSet=CharSet.Ansi)] static extern int MessageBox(int h, string m, string c, int t); public static int Main() { return MessageBox(0, "Hello World!", "Caption", 0); } }}
Unmanaged code can call back to managed code Unmanaged parameter is function pointer Must supply parameter as delegate P/Invoke creates callback thunk Passes address of thunk as callback parameter Unmanaged Code Managed Code .NET Application DLL Call passes pointer to callback function DLL function Implementation of callback function P/Invoke Callbacks
Callback Example public class EnumReport { public bool Report(int hwnd, int lParam) { // report the window handle Console.Write("Window handle is "); Console.WriteLine(hwnd); return true; }}; public class SampleClass{ delegate bool CallBack(int hwnd, int lParam); [DllImport("user32")] static extern int EnumWindows(CallBack x, int y); public static void Main() { EnumReport er = new EnumReport(); CallBack myCallBack = new CallBack(er.Report); EnumWindows(myCallBack, 0); }}
Managed Extensions for C++ • Allows C++ code to be managed by GC/CLR • compile with /clr: It Just Works • generates MSIL from C++ • contains, eg • __gc, __box, __typeof, __interface, __property • #pragma managed/unmanaged • Very useful for native access to C++ libraries • build a “managed wrapper” • Partial conversion to managed code