I would like to ask is this code thread safe? There is a problem with attachment object. It is passed by reference to the new thread where MailHelper use it and sometimes the attachment object is mixed between threads.
public static void Start()
{
foreach (var message in messages)
{
//skip code
var fileName = httpWebResponse.GetResponseHeader("filename");
var fileStream = httpWebResponse.GetResponseStream();
var attachment = new Attachment(fileStream, fileName);
var thread = new Thread(() =>
{
var dictionary = new ListDictionary
{
{ "$Url$", message.Url }
};
MailHelper.SendMessage(dictionary,
message.Mail.Headers.From.Address,
"EmailConvertSuccess.txt",
attachment)
});
thread.Start();
}
}
No this will probably not be working - but it's not only the attachment (see Darins answer) but the message object you use as an iterator as well - you will have to copy it to a local instance before calling your Thread like this:
var messageCopy = message;
new Thread(a =>
MailHelper.SendMessage(
new ListDictionary { { "$Url$", messageCopy .Url } },
messageCopy.Mail.Headers.From.Address,
"EmailConvertSuccess.txt",
a as MailAttachment)
).Start(attachment);
If you really want to you could pass this as parameter - just like Darin did with it's variant but I don't think this is really needed)
I don't see a problem with attachment. True, it's captured in a closure, but as it's declared inside the loop, there should not be any problem with that.
However, there is a problem with message. Try var message1 = message; and then use message1 in the lambda.
Related
I normally send data to event hub as follows..
var encoded = Encoding.UTF8.GetBytes(serializedString);
using (var edata = new EventData(encoded) { PartitionKey = mypkey })
{
edata.Properties[EventDataPropertyKeys.MyKey] = myvalue;
await _eventclient.SendAsync(edata).ConfigureAwait(false);
}
Today I thought of trying to send the data via batch and tried to create a list of EventData objects as follows..
List<EventData> eventDataList = new List<EventData>();
//in a loop
var encoded = Encoding.UTF8.GetBytes(serializedString);
using (var edata = new EventData(encoded) { PartitionKey = mypkey })
{
edata.Properties[EventDataPropertyKeys.MyKey] = myvalue;
eventDataList.Add(edata);
}
But when I check the eventdatalist objects, I find SerializedSizeInBytes property of EventData object showing
'This eventdata instance has already been disposed'
and while accessing throws..
'eventData.SerializedSizeInBytes' threw an exception of type 'System.ObjectDisposedException'
Any help is sincerely appreciated..
Thanks
Because in the first code snippet, you send edata inside the using block. But in the second snippet, you put edata in a list, and after that, loop through the list and send each item after the using block, where the item edata is already disposed.
I need some help with handling Tasks. I have an XML String which is deserialized into a class. The class itself contains a property, e.g. rssProvider which is set to an unique value like MSN or YAHOO. However there can be multiple values delimited with an , in this field.
I am using this deserialized class instance in multiple Tasks. However the function which gets called in this task can only work with one rssProvider value, so I have a split on that string and a foreach loop which creates the task.
In the task itself the function I call needs the full object but with one rssProvider. However when I change the value of that property to the value in the foreach the other tasks will fail as they will get the same single value from the task which runs first.
Any ideas how I should restructure the logic? Thanks!
My code:
List<Task<ResponseOBJ>> tasks = new List<Task<ResponseOBJ>>();
// get List of rssProviders
string[] providers = request.requestBody.rssProvider.Split(',');
//go through each provider
foreach (string provider in providers)
{
Task<ResponseOBJ> task = Task.Factory.StartNew<ResponseOBJ>(() =>
{
request.requestBody.rssProvider = provider;
doStuff(request);
}
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
I would create a copy constructor in your Request object which copies the content of the original Request and creates a fresh one:
public class Request
{
public Request(Request oldRequest)
{
// initalize new request from the old
}
}
And then change my code to create a new request per task:
List<Task<ResponseOBJ>> tasks = new List<Task<ResponseOBJ>>();
// get List of rssProviders
string[] providers = request.requestBody.rssProvider.Split(',');
//go through each provider
foreach (string provider in providers)
{
Task<ResponseOBJ> task = Task.Factory.StartNew<ResponseOBJ>(() =>
{
request.requestBody.rssProvider = provider;
var newRequest = new Request(request);
doStuff(newRequest);
}
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
One option might be to change doStuff(request) to doStuff(request, provider) and remove the line request.requestBody.rssProvider = provider;, and then change your doStuff accordingly.
foreach (string provider in providers)
{
Task<ResponseOBJ> task = Task.Factory.StartNew<ResponseOBJ>(() =>
{
doStuff(request, provider);
}
tasks.Add(task);
}
Another option (as also mentioned in the above comments) is to create a new request object for each provider.
I have a function in my program that creates new widgets to represent data, however whenever a widget is created i get alot of "AutoRelease with no NSAutoReleasePool in place" error messages. Since an NSAutoReleasePool should be automatically created on the main thread, I have an inkling that these error messages appear because an async function might create my threads...
This is the function called to create widgets to represent the latest information. This function is called pretty often:
private void CreateAndDisplayTvShowWidget (TvShow show)
{
var Widget = new TvShowWidgetController (show);
Widget.OnRemoveWidget += ConfirmRemoveTvShow;
Widget.View.SetFrameOrigin (new PointF (0, -150));
Widget.View.SetFrameSize (new SizeF (ContentView.Frame.Width, 150));
ContentView.AddSubview (Widget.View);
show.ShowWidget = Widget;
}
This function is usually called when this async function returns:
private static void WebRequestCallback (IAsyncResult result)
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse (result);
StreamReader responseStream = new StreamReader (response.GetResponseStream ());
string responseString = responseStream.ReadToEnd ();
responseStream.Close ();
ProcessResponse (responseString, request);
}
ProcessResponse (responseString, request) looks like this:
private static void ProcessResponse (string responseString, HttpWebRequest request)
{
string requestUrl = request.Address.ToString ();
if (requestUrl.Contains (ShowSearchTag)) {
List<TvShow> searchResults = TvDbParser.ParseTvShowSearchResults (responseString);
TvShowSearchTimeoutClock.Enabled = false;
OnTvShowSearchComplete (searchResults);
} else if (requestUrl.Contains (MirrorListTag)) {
MirrorList = TvDbParser.ParseMirrorList (responseString);
SendRequestsOnHold ();
} else if (requestUrl.Contains (TvShowBaseTag)) {
TvShowBase showBase = TvDbParser.ParseTvShowBase (responseString);
OnTvShowBaseRecieved (showBase);
} else if (requestUrl.Contains (ImagePathReqTag)) {
string showID = GetShowIDFromImagePathRequest (requestUrl);
TvShowImagePath imagePath = TvDbParser.ParseTvShowImagePath (showID, responseString);
OnTvShowImagePathRecieved (imagePath);
}
}
CreateAndDisplayTvShowWidget (TvShow show) is called when the event OnTvShowBaseRecieved (TvShow) is called, which is when I get tons error messages regarding NSAutoReleasePool...
The last two functions are part of what is supposed to be a cross-platform assembly, so I can't have any MonoMac-specific code in there...
I never call any auto-release or release code for my widgets, so I assume that the MonoMac bindings does this automatically as part of its garbage collection?
You can create autorelease pools at point within the call stack, you can even have multiple nested autorelease pools with the same call stack. So you should be able to create your autorelease pools in the async entry functions.
You only need an NSAutoreleasePool if you use the auto-release features of objects. A solution is to create a NSAutoreleasePool around the code that manipulates auto-released objects (in the async callback).
Edit:
Have you tried to encapsulate the creation code with a NSAutoreleasePool ? As this is the only place where you call MonoMac code, this should solve the issue.
private void CreateAndDisplayTvShowWidget (TvShow show)
{
using(NSAutoreleasePool pool = new NSAutoreleasePool())
{
var Widget = new TvShowWidgetController (show);
Widget.OnRemoveWidget += ConfirmRemoveTvShow;
Widget.View.SetFrameOrigin (new PointF (0, -150));
Widget.View.SetFrameSize (new SizeF (ContentView.Frame.Width, 150));
ContentView.AddSubview (Widget.View);
show.ShowWidget = Widget;
}
}
Note that even if you don't use auto-released objects directly, there are some case where the Cococa API use them udner the hood.
I had a similar problem and it was the response.GetResponseStream that was the problem. I surrounded this code with...
using (NSAutoreleasePool pool = new NSAutoreleasePool()) {
}
... and that solved my problem.
I have a function to download a mailmessage as MSG file from DocuShare server. The function works perfectly when called from a main thread. However, when I call the function in a separate thread, the download fails. When I step in to the code, I can see that the function is being called, all the parameters are evaluated correctly and the return value is what I expect. Unfortunately, I see, no files get downloaded.
Codes:
private void btnDownloadMails_Click(object sender, EventArgs e)
{
//Thread t = new Thread(new ThreadStart(DownloadMailAsMsg));
//t.Start(); //Does not work
DownloadMailAsMsg(); // Works fine
}
void DownloadMailAsMsg()
{
DSServerMap.Server dsserver = new DSServerMap.Server();
if (!SelectMappedServer(ref dsserver, textServer.Text.ToString()))
return;
long status = 0;
dsserver.DocuShareAddress = textServer.Text;
dsserver.UserName = textUser.Text;
dsserver.Password = textPwd.Text;
status = dsserver.Logon();
if (status == 0)
{
IItemObj objParentItem;
string[] emailHan = { "MailMessage-12", "MailMessage-13", "MailMessage-31" };
foreach (string handnum in emailHan)
{
objParentItem = (IItemObj)dsserver.CreateObject(handnum);
DSGATEWAYLib.IGatewayHandler gateway = (DSGATEWAYLib.IGatewayHandler)dsserver.Open();
objParentItem.AttachGateway(gateway, true);
objParentItem.Name = #"D:\em\m_" + handnum + ".msg";
int flag = objParentItem.DSDownload(0);
}
}
}
Any ideas?
Thanks
Prakash
Maybe you need a STA thread for this. I had a similar problem once and the following solved my problem:
Thread t = new Thread((ThreadStart)delegate
{ // MAPI does only work in STA threads. Therefore an STA thread needs to be created explicitly for the SendMail call.
//...do work here
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
Maybe this will solve your problem as well.
Your thread should be a class member instead of a method variable.
When your method completes, the thread variable goes out of scope and could get cleaned up without completing.
You are trying to access Control's properties in non UI threads,
for example in lines,
dsserver.DocuShareAddress = textServer.Text;
dsserver.UserName = textUser.Text;
dsserver.Password = textPwd.Text;
you are trying to access UI Control's Text properties in different thread, which actually throws an exception.
Each of the control's values you want to access in different thread, you have to wrap it in some sort of arguements and pass it to the thread.
class MyServerParameters{
string Server;
string Username;
string Password;
}
private void btnDownloadMails_Click(object sender, EventArgs e)
{
MyServerParameters p = new MyServerParameters();
// we are still in UI thread so copy your values
// to p
p.Server = textServer.Text;
p.Username = textUser.Text;
p.Password = textPwd.Text;
Thread t = new Thread(new ParametricThreadStart(DownloadMailAsMsg));
// pass p to another thread
t.Start(p); // this will work...
}
void DownloadMailAsMsg(object mp)
{
// access p back like this...
MyServerParameters p = mp as MyServerParameters;
dsserver.DocuShareAddress = p.Server;
dsserver.UserName = p.Username;
dsserver.Password = p.Password;
Create a copy of .Text properties of the controls and reference only them in your second thread.
You'll lock your application or get an exception if you use different thread to access any of the controls.
Other way around is to use .Invoke(), but in your case you really don't need to go there.
If I use code like this [just below] to add Message Headers to my OperationContext, will all future out-going messages contain that data on any new ClientProxy defined from the same "run" of my application?
The objective, is to pass a parameter or two to each OpeartionContract w/out messing with the signature of the OperationContract, since the parameters being passed will be consistant for all requests for a given run of my client application.
public void DoSomeStuff()
{
var proxy = new MyServiceClient();
Guid myToken = Guid.NewGuid();
MessageHeader<Guid> mhg = new MessageHeader<Guid>(myToken);
MessageHeader untyped = mhg.GetUntypedHeader("token", "ns");
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
proxy.DoOperation(...);
}
public void DoSomeOTHERStuff()
{
var proxy = new MyServiceClient();
Guid myToken = Guid.NewGuid();
MessageHeader<Guid> mhg = new MessageHeader<Guid>(myToken);
MessageHeader untyped = mhg.GetUntypedHeader("token", "ns");
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
proxy.DoOtherOperation(...);
}
In other words, is it safe to refactor the above code like this?
bool isSetup = false;
public void SetupMessageHeader()
{
if(isSetup) { return; }
Guid myToken = Guid.NewGuid();
MessageHeader<Guid> mhg = new MessageHeader<Guid>(myToken);
MessageHeader untyped = mhg.GetUntypedHeader("token", "ns");
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
isSetup = true;
}
public void DoSomeStuff()
{
var proxy = new MyServiceClient();
SetupMessageHeader();
proxy.DoOperation(...);
}
public void DoSomeOTHERStuff()
{
var proxy = new MyServiceClient();
SetupMessageHeader();
proxy.DoOtherOperation(...);
}
Since I don't really understand what's happening there, I don't want to cargo cult it and just change it and let it fly if it works, I'd like to hear your thoughts on if it is OK or not.
I think your refactored code doesn't put any added-value. Have you taken in account that the OperationContext can be null?
I think this will be a safer approach:
using(OperationContextScope contextScope =
new OperationContextScope(proxy.InnerChannel))
{
.....
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
proxy.DoOperation(...);
}
OperationContextScope's constructor will always cause replacement of the Operation context of the current thread; The OperationContextScope's Dispose method is called which restores the old context preventing problems with other objects on the same thread.
I believe your OperationContext is going to get wiped each time you new the proxy.
You should plan on adding the custom message headers prior to each call. This is good practice in any case as you should prefer per call services and close the channel after each call.
There are a couple patterns for managing custom headers.
You can create the header as part of the constructor to the proxy.
Alternatively, you can extend the binding with a behavior that automatically adds the custom header prior to making each call. This is a good example: http://weblogs.asp.net/avnerk...