AppDomain

Nov 30, 2012 at 7:57 PM

Found how to create a thread  :)

Nov 30, 2012 at 10:03 PM
Edited Dec 1, 2012 at 10:47 AM

Let get us started from the base,
   What is an application? .. Good question hein !!

It can basicaly be anything  ...
In fact, it is only a block of memory that the operating system has isolated.

Usualy Whatever software that has request this block of memory from the OS, usualy load an assembly(exe) find in the header of the .EXE file the address of the entry point and start a thread at this location ...

In a managed exe, at this entry point, you find always the same code, it is a little unmanaged function that loads another assembly "mscoree.dll", and calls in this dll another unmanaged function that will start the CLR.

The main thing to understand is that an application is only an isolated block of memory.

Why Isolated?

The reason is simple, it is isolated to insure that whatever happen in this block, (it get filled, it get erased, the thread crask) or whatever, it does not affect what is going on into another isolated block (Which mean into another application).

------------
Now, what is an appDomain ? what your application creates when it creates an AppDomain?

Basicaly, it creates another Isolated block of memory. (or another application inside your application)

What you should have see by now, is that the rule stay true, "Whatever happen in the application domain we created will in no way affect our application because our application and the AppDomain are two different isolated block of memory"

-----
to create an App domain, it is simple
   Instance = AppDomain("SomeName")

-----

Now, we have created it? We want to put some code in it and run it .. but how?

Before we answer that, let think about Why did we created this?

----

there is two main reason to create this
   1) we want to run some code that will or likely will crash and want to do it without any risks to crash our application. So, you run the code into the AppDomain, if it crash, it will do it without affecting our application

   2) we want to load some code dynamicaly, some assemblies dynamicaly  and want to be a be able to unload it.
Well, here is a fact of life, you can load what ever assembly you want, into an isolated block... but you can never unload it.. it is there for the life of the application and whatever memory it uses, can never be free again.

     But when you create an AppDomain, you can release it back to the OS when you are done with it (So the OS can re-use it for another application or for whatever it need to do with the memory)

     Therefore, if you load the assembly into the AppDomain, when you release the AppDomain, you are in the same time freeing the memory used by the assembly you loaded.

To release the memory used by the AppDomain,
     AppDomain.Unload(Instance)

HOWEVER THIS IS IMPORTANT ..
 if you create at any time from your application a reference to something inside the AppDomain, or if the AppDomain creates an any time a reference to something inside your application, then forget it the AppDomain memory becomes impossible to release.

----
  Now, keep in mind this important note and let go back to How to put some code in the AppDomain

To put something into the AppDomain use the method,
   AppDomain.CreateInstanceAndUnwrap

What this does, it create an Instance of the specified class, wrap it into a serializable object, then the marshaler takes the wrapper, marshal it to the other block of memory and unwrap it. And there it is !!

But, no, it is not so simple.

when the function "CreateInstanceAndUnwrap" ran, when it created the instance, it created it in your heap, and what was wrapped is not the instance, but the reference to the instance in your heap.

Therefore what was unwrapped in the application domain is a reference to the instance in the head....

And what did we say earlyer? if we get into the AppDomain a reference to something inside our application, the AppDomain cannot be unload anymore ..

So, it dont work :(

This is where the MarshalByRefObject come in scoope
----

First lets look at what the marshaller does when it marshal something "ByRef"

(What follows is also true for the arguments when calling an API and in the marshalling in COM)

The Marshaller will make a copy of the object passed ByRef into the memory of the callee, not a copy of the reference, but a copy of the object itself

then when the callee update or change the values in the object, those changes are not immediatly updated in the original copy in the caller memory.

What happen instead, the marshaller will wait for the function called to return, and only at this time it will make copy of of the object in the callee memory and will go to replace the original object in the caller heap with the updated object

(Just a note here, the marshaller need to be able to serialize the object to marshal it ... this is always true ... So, the object must be decorated with <Serializable>)

----
What is interresting in this way the marshaller handle ByRef object, is the fact that there is no reference created at any time between the caller and the callee

And, like its name let it understand, A MarshalByRefObject (or any object that inherits from it) is marshalled byRef, not ByVal like it would be for any other type of object

----
 
Knowing this, it becomes obvious now that when we call   "AppDomain.CreateInstanceAndUnwrap"
the object that we want to to create and unwrap into our AppDomain is a MarshalByRefObject

This way, no reference are created between the AppDomain and our application and the AppDomain can be unloaded

Dec 1, 2012 at 5:33 AM

This is a really good description of whats going on!(where's the helpful button?)

So in a nutshell, we create a temporary application domain memory block, load our assemblies, marshallbyrefobj back to the main app, then unload that instance?

Dec 1, 2012 at 12:41 PM
Edited Dec 1, 2012 at 1:48 PM

 

I would like to add a few comment about AppDomain

   When the OS create one of those isolated block, it has to have a mecanism to reclaim them when not used anymore.

Aldo the exact process for this is quite complicated, it can be explain simply:

------

The OS will reclaim the block of memory (destroy the application) when the application has no more owner

The only thing that can own a block of memory is a thread.

A block of memory can be own by one thread or by many theads (but not all threads running into the application are owner of the block)

      Let comment on this last statement:

     If you have play a bit with "Threading" you have noticed that all threads has the property "IsBackground".

     Well, when you are setting this property to False   "MyThread.IsBackground = False"

     You are in fact telling the OS  "This thread is an owner of my application"

     And since this thread becomes owner, as long this thread will be alive, the OS will not reclaim the application

     here a code that show this

    in the code, the UI thread creates a new thread and set it as another owner of the application an puts this thread to sleep for 5 seconds

    while the thread sleep, the UI thread kills itself and crashes the application (Aborting the UI thread crashes the message pump, invalidate all the handles to every form and control, crashes the Windows class .. Well, it is the most catastrophic event that can happen in an application)

   but since the new thread is an owner , when it will wake up after 5 seconds,  what does it find?

   it will find that the block of memory is still there, the assembly is still there, nothing has been remove. It is just there without nothing going on in it.

   and it can start to runs whatever it want

-------------------------------

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'The UI thread creates a new thread
        Dim TH As New Threading.Thread(AddressOf NewThreadMain)

        'The UI thread set the new thread as an owner of the application
        TH.IsBackground = False

        'And start the new thread
        TH.Start()

        'Here, the UI thread and the new thread are owner of thee block of memory

        'And finaly, the UI thread kills itself.
        'This should crash crash the application
        Threading.Thread.CurrentThread.Abort()

        'At this point, only the new thread still exists in the application ... 
        'and is still sleeping for a little 5 seconds, but when it will wakeup .. 

    End Sub


    Private Sub NewThreadMain()

        Threading.Thread.Sleep(5000)

        'the new thread can do whatever it wants
        'but here, let it start a new message pump and create a new form1
        Application.Run(New Form1)
    End Sub
End Class

 

Now, change the line in the code for this

                   TH.IsBackground = True

And run the code again, now, TH is not an owner anymore, so, when the UI thread will kill itself, the OS will claim back the block of memory, and the new thread will die with it

Well this is not a programming pattern that you should use (I mean crashing a thread that own a message loop, handles, windows, controls .. etc),

 It realy show how it works about memory block and ownership, but it is not freindly with the OS

--------------------------

     So, when you create an AppDomain, the thread that creates it becomes owner of the memory block. It realy doesn't matters if this thread ever runs any code in the block itself, You can create other threads to go in the block to run some code if you whish, .. but when the thread dies, the AppDomain will becomes without owner and the OS will claim the memory back (Unless of course, if this thread as created other thread that were set as owner ( with  the property IsBackground)

-------------------------

 A ThreadPool thread can never be owner of a block of memory.

If you need a threadPool that has some threads that can becomes owner, you need to implement your own thread pool

Dec 1, 2012 at 2:29 PM
Edited Dec 1, 2012 at 10:27 PM

 

Well .. I was talking about implementing your own pool thread ...

Here an example if you ever need this

-----------

Friend Class ThreadPool

    Private Lock As New Object
    Private ARE As New Threading.AutoResetEvent(False)
    Private Q As New MyQueue(ARE)
    Public Property MaxThread As Integer

    Private _GetAvailableThreads As Integer
    Public Property GetAvailableThreads As Integer
        Get
            Return _GetAvailableThreads
        End Get
        Private Set(ByVal value As Integer)
            _GetAvailableThreads = value
        End Set
    End Property


    Public Sub New(ByVal MaxThread As Integer)
        For x As Integer = 1 To MaxThread
            Dim th As New Threading.Thread(AddressOf Pool)
            th.IsBackground = True
            th.Start()
            GetAvailableThreads = MaxThread
            Me.MaxThread = MaxThread
        Next
    End Sub

    Private Sub Pool()
        Do
            SyncLock Lock
                ARE.WaitOne()
            End SyncLock
            If Q.Count > 0 Then
                Dim K As KeyValuePair(Of [Delegate], Object)
                K = DirectCast(Q.Dequeue, KeyValuePair(Of [Delegate], Object))
                Threading.Interlocked.Decrement(GetAvailableThreads)
                K.Key.DynamicInvoke(K.Value)
                Threading.Interlocked.Increment(GetAvailableThreads)
            End If

        Loop
    End Sub

    Public Sub QueueUserWorkItem(ByVal Callback As Action(Of Object), ByVal State As Object)
        Dim K As New KeyValuePair(Of [Delegate], Object)(Callback, State)
        Q.Enqueue(K)
    End Sub



    Private Class MyQueue : Inherits Queue

        Private ARE As Threading.AutoResetEvent

        Public Sub New(ByVal Are As Threading.AutoResetEvent)
            Me.ARE = Are
        End Sub

        Public Overrides Sub Enqueue(ByVal obj As Object)
            MyBase.Enqueue(obj)
            ARE.Set()
        End Sub
    End Class
End Class

------------
example of usage:
-------
Public Class Form1

    Private MyPool As New ThreadPool(10)

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        MyPool.QueueUserWorkItem(AddressOf DoWork, "A Paramater")
    End Sub

    Private Sub DoWork(ByVal State As Object)
        Beep()
    End Sub

End Class
Dec 1, 2012 at 3:55 PM

This is all some pretty useful information!

Dec 1, 2012 at 4:07 PM

Just to let you know a little bit about me, I started programming quick basic as a hobby when I was only 7 and I'm 36 now. But--- I have no formal education in software programming. So I'm pretty good with the basics, but I do have a long way to go. I would say that I am probably an intermediate level overall, anyways I hope none of this bothers you, because it's obvious that you are an expert!

Dec 1, 2012 at 6:34 PM
Edited Dec 1, 2012 at 6:38 PM

 

I have started programming at around 15, .. in 1975

I worked for a time with Fortran

then I worked on these new thing -- the IBM PC --- done some assembler

and started my compagny ( programming mostly with Pascal and C )

when Windows started to be real strong on the market, I started to use Delphi and stay with that until 2008 when I started to look at managed code

I first learned C#, ... but I liked a lot the VB syntax .. so in 2009 I decided to start to answer question on the forum, just to learn VB

 

I stopped programming professionaly in 2009 .. got a job from a japanese compagny and been posted in Charlotte NC (Project manager for civil engineering works)

 Actually, I have been running some project for them since 2001

Dec 1, 2012 at 7:01 PM

1975... You're a pioneer! Whats your company called? Microsoft? haha just a joke, but that is quite impressive!