You misunderstand the definitions of AppDomain , Process and Thread - AppDomains always lives in one , but one process can be a master for different AppDomains *:
The ability to run multiple applications within the same process significantly increases the scalability of the server.
- this is similar to
Thread , just for mention.
So, the answer to your question is Yes , subtle synchronization primitives will work with various AppDomains , since there are no end-to-end calls in this architecture, but only a logical difference for different processes.
Regarding the related issue, there are no problems with synchronization primitives there, only with the SynchronizationContext , which Thread specific , but not AppDomain specific.
You can find a great answer about the difference between AppDomain , Thread and Process here: The difference between AppDomain, Assembly, Process and Thread
As for unloading the AppDomain, I think you can reorganize your code, since you can run the working Thread in your AppDomain with limited access to system resources and just wait until it is completed, for example, it is mentioned here :
using System; using System.Threading; using System.Threading.Tasks; class Program { static void Main() { var ad = AppDomain.CreateDomain("WhereTheWorkHappens"); Task<string> t = DoWorkInOtherDomain(ad); Console.WriteLine("waiting..."); Console.WriteLine(t.Result); Console.ReadLine(); } static Task<string> DoWorkInOtherDomain(AppDomain ad) { var ch = new MarshaledResultSetter<string>(); Worker worker = (Worker)ad.CreateInstanceAndUnwrap(typeof(Worker).Assembly.FullName, typeof(Worker).FullName); worker.DoWork(ch); return ch.Task; } class Worker : MarshalByRefObject { public void DoWork(MarshaledResultSetter<string> callback) { ThreadPool.QueueUserWorkItem(delegate { Thread.SpinWait(500000000); callback.SetResult(AppDomain.CurrentDomain.FriendlyName); }); } } class MarshaledResultSetter<T> : MarshalByRefObject { private TaskCompletionSource<T> m_tcs = new TaskCompletionSource<T>(); public void SetResult(T result) { m_tcs.SetResult(result); } public Task<T> Task { get { return m_tcs.Task; } } } }
As an additional idea for you, you can read about the sandbox with the TPL code , which, it seems to me, is better since you do not manually manage system resources and are less likely to be hacked by your untrusted code.
Alternatively, you can find the GitHub project with Cross-AppDomain Marshaling friendly TPL wrappers for APM