Threadlocal and waiting

So, we have a registration system in which we use Log.Info and write it to ILogger .

Now we have several workers working in the background, and we want them to write them in their journals. So everything is bundled for Desktop. Everything that is registered during this task should be redirected to its own registrar.

Where we are thinking about creating the Log.SetLoggerForCurrentThread method, we implement it using ThreadLocal. The executing code will look something like this:

 public class Worker { ILogger _Logger; public void ExecuteTask() { Log.Info( "This goes to the 'Global' logger" ); using ( Log.SetLoggerForCurrentThread(_Logger) ) { Log.Info( "This goes to local logger" ); DoWork(); } } private async void DoWork() { Log.Info( "Starting..." ); // SomeMethod does some logging, // that also needs to be forwared to the local logger var value = await SomeDeepDomainClass.SomeMethod(); // if we use ThreadLocal, and this thread has been reused, // it could be a completely different logger that is now attached. Log.Info( "Ended..." ); } } 

Questions

  • When we use the wait, the thread can theoretically handle the work of another worker, thereby mixing local registrars.
  • What is the best way to do something like this? What storage method can I use?
  • How does CultureInfo handle this?

Background Information

Most of these Workers will work on an Azure WorkerRole instance, but now and in addition, they will also be launched (once) from the console application.

+7
multithreading c # async-await
source share
2 answers

You can use CallContext to transfer (serializable) data over streams. See this article for an example:

http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html

For some reference information see this article:

http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx

+6
source share

In my opinion, the best solution is to either pass the log instances as arguments (or member variables) or enter them (for example, using nested areas).

However, if you want to save and transfer the log instance implicitly in an await compatible manner, you will need to use a boolean call context. I have a blog post describing this approach that points out the limitations of this approach:

  • It works only on the full .NET 4.5 platform.
  • You must use the rewrite semantics. This usually means saving only immutable data.

With this in mind, here is a code that should work for your needs:

 public static class LocalLogger { private static readonly string name = Guid.NewGuid().ToString("N"); // Static Log methods should read this. public static ILogger CurrentLogger { public get { var ret = CallContext.LogicalGetData(name) as ILogger; return ret == null ? Logger.GlobalLogger : ret; } private set { CallContext.LogicalSetData(name, value); } } // Client code uses this. public static IDisposable UseLogger(ILogger logger) { var oldLogger = CurrentLogger; CurrentLogger = logger; if (oldLogger == GlobalLogger) return NoopDisposable.Instance; return new SetWhenDisposed(oldLogger); } private sealed class NoopDisposable : IDisposable { public void Dispose() { } public static readonly Instance = new NoopDisposable(); } private sealed class SetWhenDisposed : IDisposable { private readonly ILogger _oldLogger; private bool _disposed; public SetWhenDisposed(ILogger oldLogger) { _oldLogger = oldLogger; } public void Dispose() { if (_disposed) return; CurrentLogger = _oldLogger; _disposed = true; } } } 
+6
source share

All Articles