I have been bitten by a poorly architected solution. It is not thread safe!
I have several shared classes and members in the solution, and during development all was cool...
BizTalk has sunk my battle ship.
We are using a custom BizTalk Adapter to call my assemblies. The Adapter is calling my code and running things in parallel, so I assume it is using multiple threads all under the same AppDomain.
What I would like to do is make my code run under its own AppDomain so the shared problems I have will not muck with each other.
I have a very simple class that the BizTalk adapter is instantiating then running a Process() method.
I would like to create a new AppDomain inside my Process() method, so each time BizTalk spins another thread, it will have its own version of the static classes and methods.
BizTalkAdapter Code:
// this is inside the BizTalkAdapter and it is calling the Loader class //
private void SendMessage(IBaseMessage message, TransactionalTransmitProperties properties)
{
Stream strm = message.BodyPart.GetOriginalDataStream();
string connectionString = properties.ConnectionString;
string msgFileName = message.Context.Read("ReceivedFileName", "http://schemas.microsoft.com/BizTalk/2003/file-properties") as string;
Loader loader = new Loader(strm, msgFileName, connectionString);
loader.Process();
EventLog.WriteEntry("Loader", "Successfully processed: " + msgFileName);
}
This is the class BizTalk Calls:
public class Loader
{
private string connectionString;
private string fileName;
private Stream stream;
private DataFile dataFile;
public Loader(Stream stream, string fileName, string connectionString)
{
this.connectionString = connectionString;
this.fileName = fileName;
this.stream = stream;
}
public void Process()
{
//***** Create AppDomain HERE *****
// run following code entirely under that domain
dataFile = new DataFile(aredStream, fileName, connectionString);
dataFile.ParseFile();
dataFile.Save();
// get rid of the AppDomain here...
}
}
FYI: The Loader class is in a seperate DLL from the dataFile class.
Any help would be appreciated. I will continue to working on making the code Thread-Safe, but I feel like this could be the "simple" answer.
If anyone has any other thought, please throw in.
Thank you,
Keith
Just for completeness.
I did find that if I marked the send adapter as "Ordered Delivery" in the "Transport Advanced Options" dialog I was able to avoid the multi-thread issues I was having.
I figure this is another possible answer to my problem, but not necessarily to the question.
Using app domains you could do something like this:
public class Loader
{
private string connectionString;
private string fileName;
private Stream stream;
private DataFile dataFile;
public Loader(Stream stream, string fileName, string connectionString)
{
this.connectionString = connectionString;
this.fileName = fileName;
this.stream = stream;
}
public void Process()
{
//***** Create AppDomain HERE *****
string threadID = Thread.CurrentThread.ManagedThreadId.ToString();
AppDomain appDomain = AppDomain.CreateDomain(threadID);
DataFile dataFile =
(DataFile) appDomain.CreateInstanceAndUnwrap(
"<DataFile AssemblyName>",
"DataFile",
true,
BindingFlags.Default,
null,
new object[]
{
aredstream,
filename,
connectionString
},
null,
null,
null);
dataFile.ParseFile();
dataFile.Save();
appDomain.Unload(threadID);
}
}
Which bit, exactly, is being a pain in terms of thread safety? I can't see any static state nor singletons - and there seems to be appropriate "new" objects... am I being blind?
So what is the symptom you are seeing...
An AppDomain answer will be (relatively) slow. As part of a middleware-backed system this might be OK (i.e. the "relatively" is in the same ball-park).
If you do have some static state somewhere, another option that sometimes works is [ThreadStatic] - which the runtime interprets as "this static field is unique per thread". You need to be careful with initialization, though - the static constructor on thread A might assign a field, but then thread B would see a null/0/etc.
Why not just put a lock around the code you want to execute sequentially? It will be a bottleneck, but it should work in a multithreaded environment.
public class Loader
{
private static object SyncRoot = new object();
private string connectionString;
private string fileName;
private Stream stream;
private DataFile dataFile;
public Loader(Stream stream, string fileName, string connectionString)
{
this.connectionString = connectionString;
this.fileName = fileName;
this.stream = stream;
}
public void Process()
{
lock(SyncRoot) {
dataFile = new DataFile(aredStream, fileName, connectionString);
dataFile.ParseFile();
dataFile.Save();
}
}
}
If you have shared statics that are conflicting with each other, then you might want to try adding [ThreadStatic] attribute to them. This will make them local to each thread. That may solve your problem in the short term. A correct solution would be to simply rearchitect your stuff to be thread-safe.
Just for completeness.
I did find that if I marked the send adapter as "Ordered Delivery" in the "Transport Advanced Options" dialog I was able to avoid the multi-thread issues I was having.
I figure this is another possible answer to my problem, but not necessarily to the question.
Creating and tearing down an appdomain for each call - I take it you're not worried about performance on this one?
Ideally you should change the called code to be threadsafe.
来源:https://stackoverflow.com/questions/307292/how-to-use-an-appdomain-to-limit-a-static-class-scope-for-thread-safe-use