I have a site I manage for a client and they wanted to be able to send out emails to all of their membership. I contacted the host and they suggested writing it in a way that it sends out in batches of 50 or less every minute so the mail server doesn't get overloaded.
That sounds great but the only way I could think of to do this without causing the administrator to have to sit on a page while it sends emails and reloads between each batch was to have a page call an ashx handler which fired up a thread to do the work and the thread is set to sleep after each batch for 60 seconds.
When I run the code from my machine it works fine and completes the entire list of emails. When I run it from the web host, which I don't have access to aside from ftp, it nearly completes but doesn't. Then if I try to hit the ashx page again to finish any that weren't sent, it doesn't do anything. It's like the thread causes something to lock up maybe and keeps additional threads from running.
Here's the code I'm using and I've never used threading before... so, does anyone know why it might be doing this and how to make it work correctly? Do I need to specifically kill the thread after I'm done? If so, how? Thanks.
public void ProcessRequest(HttpContext context)
{
if (context.Request.QueryString["id"].IsValid<int>())
{
campaignId = context.Request.QueryString["id"].To<int>();
var t = new Thread(new ThreadStart(SendEmails))
{
Priority = ThreadPriority.Lowest
};
t.Start();
}
}
private void SendEmails()
{
int currentCount = 0;
BroadcastEmailCampaign campaign = EmailController.GetCampaign(campaignId, false);
List<Member> memberlist = EmailController.GetEmailList(campaign.CampaignId);
var message = new MailMessage
{
Body = campaign.Body,
From = new MailAddress(campaign.SentBy),
IsBodyHtml = true,
Subject = campaign.Subject,
BodyEncoding = Encoding.UTF8
};
//add attachment
if (!string.IsNullOrEmpty(campaign.Attachment) && File.Exists(campaign.Attachment))
{
var attachment = new Attachment(campaign.Attachment);
EmailAttachmentType.SetContentProperites(campaign.Attachment, ref attachment);
message.Attachments.Add(attachment);
}
if (memberlist.Count <= 0)
{
return;
}
bool sendingComplete = false;
EmailController.SetCampaignSendingStatus(true, campaign.CampaignId);
while (sendingComplete == false)
{
message.Bcc.Clear();
message.To.Clear();
message.To.Add(new MailAddress(dummyEmailAddress));
List<Member> emailsToSend = memberlist.Skip(currentCount).Take(takeCount).ToList();
if (emailsToSend.Count <= 0)
{
sendingComplete = true;
EmailController.LogEmailCampaignResult(campaign);
EmailController.SetCampaignSendingStatus(false, campaign.CampaignId);
}
if (!sendingComplete)
{
foreach (Member email in emailsToSend)
{
message.Bcc.Add(new MailAddress(email.Email));
campaign.SentTo.Add(new BroadcastEmailCampaignSentTo
{
MemberId = email.MemberId,
Email = email.Email,
DateSent = DateTime.Now
});
}
EmailController.SendEmail(message);
EmailController.LogEmailsSent(emailsToSend, campaignId);
currentCount += takeCount;
Thread.Sleep(pauseTime);
}
}
}
Since I read a lot of threading in ASP.NET and still have no real clue of the dos and donts, I usually solve tasks like you describe by a console application that runs as a Scheduled Task in Windows Task Scheduler every e.g. 5 minutes:
In the ASP.NET page, I write all required information into a database table.
The scheduler periodically polls the database table for new jobs (e.g. sending of an e-mail) and processes, then empties the database table that serves as a queue.
This enables my application to stay responsive and in addition I don't have to worry that an IISRESET or something like this would kill my background threads.
t.IsBackground=true;
If that doesn't do it, I suggest using the ThreadPool with QueueUserWorkItem.
Related
I'm running into a problem sending massive requests to a .NET Core web service. I'm using a SemaphoreSlim to limit the number of simultaneous requests. When I get a 10061 error (the web service has refused the connection), I want to dial back the number of simultaneous requests. My idea at the moment is to de-reference the SemaphoreSlim and create another:
await this.semaphoreSlim.WaitAsync().ConfigureAwait(false);
counter++;
Uri uri = new Uri($"{api}/{keyProperty}", UriKind.Relative);
string rowVersion = string.Empty;
try
{
HttpResponseMessage getResponse = await this.httpClient.GetAsync(uri).ConfigureAwait(false);
if (getResponse.IsSuccessStatusCode)
{
using (HttpContent httpContent = getResponse.Content)
{
JObject currentObject = JObject.Parse(await httpContent.ReadAsStringAsync().ConfigureAwait(false));
rowVersion = currentObject.Value<string>("rowVersion");
}
}
}
catch (HttpRequestException httpRequestException)
{
SocketException socketException = httpRequestException.InnerException as SocketException;
if (socketException != null && socketException.ErrorCode == PutHandler.ConnectionRefused)
{
this.semaphoreSlim = new SemaphoreSlim(counter * 90 / 100, counter * 90 / 100);
}
}
}
finally
{
this.semaphoreSlim.Release();
}
If I do this, what will happen to the other tasks that are waiting on the Semaphore that I just de-referenced? My guess is that nothing will happen until the object is garbage collected and disposed.
A SemaphoreSlim (just like any other object in .NET) will exist as long as there are references to it.
However, there is a bug in your code: the SemaphoreSlim being released is this.semaphoreSlim, and if this.semaphoreSlim is changed between being acquired and being released, then the code will release a different semaphore than the one that was acquired. To avoid this problem, copy this.semaphoreSlim into a local variable at the beginning of your method, and acquire and release that local variable.
More broadly, there's a difficult in the attempted solution. If you start 1000 tasks, they will all reference the old semaphore and ignore the updated this.sempahoreSlim. So you'd need a separate solution. For example, you could define a disposable "token" which is permission to call the API. Then have an asynchronous collection of these tokens (e.g., a Channel). This gives you full control over how many tokens are released at once.
I have a form with hundreds of check boxes and dropdown menus (Which value of many of them are coupled together). In the action there is updating mechanism to update an object in Session. This object does all validation and coupling of values, for example if user types %50 in one input filed, we might add 3 new SelectListItem to a dropdown.
Everything works fine, but if use starts to clicking on check boxes very quick (which is the normal case in our scenario), controller get multiple posts while it is processing previous ones. Fortunately we are only interested in the last POST, so we need a way to abort\cancel on going requests when newer request from same form comes.
What I tried:
1- blocking client side to make multiple posts when server still working on previous one. It is not desirable because it makes noticeable pauses on browser side.
2- There are several solutions for blocking multiple post backs by using HASH codes or AntiForgeryToken. But they don't what I need, I need to abort on-going thread in favor of new request, not blocking incoming request.
3- I tried to extend pipeline by adding two message handlers (one before action and another after executing action) to keep a hash code (or AntiForgeryToken) but problem is still there, even I can detect there is on-going thread working on same request, I have no way to abort that thread or set older request to Complete.
Any thoughts?
The only thing you can do is throttle the requests client-side. Basically, you need to set a timeout when a checkbox is clicked. You can let that initial request go through, but then any further requests are queued (or actually dropped after the first queued request in your scenario) and don't run that until the timeout clears.
There's no way to abort a request server-side. Each request is idempotent. There is no inherent knowledge of anything that's happened before or since. The server has multiple threads fielding requests and will simply process those as fast as it can. There's no order to how the requests are processed or how responses are sent out. The first request could be the third one that receives a response, simply due to how the processing of each request goes.
You are trying to implement transactional functionality (i.e. counting only the last request) over an asynchronous technology. This is a design flaw.
Since you refuse to block on the client side, you have no method by which to control which requests process first, OR to correctly process the outcome again on the client-side.
You might actually run into this scenario:
Client sends Request A
Server starts processing Request B
Client sends Request B
Server starts processing Request B
Server returns results of Request B, and client changes accordingly
Server returns results of Request A, and client changes accordingly (and undoes prior changes resulting from Request B)
Blocking is the only way you can ensure the correct order.
Thanks for your help #xavier-j.
After playing around this, I wrote this. Hope it be useful for someone who needs same thing:
First you need add this ActionFilter
public class KeepLastRequestAttribute : ActionFilterAttribute
{
public string HashCode { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
Dictionary<string, CancellationTokenSource> clt;
if (filterContext.HttpContext.Application["CancellationTokensDictionary"] != null)
{
clt = (Dictionary<string, CancellationTokenSource>)filterContext.HttpContext.Application["CancellationTokensDictionary"];
}
else
{
clt = new Dictionary<string, CancellationTokenSource>();
}
if (filterContext.HttpContext.Request.Form["__RequestVerificationToken"] != null)
{
HashCode = filterContext.HttpContext.Request.Form["__RequestVerificationToken"];
}
CancellationTokenSource oldCt = null;
clt.TryGetValue(HashCode, out oldCt);
CancellationTokenSource ct = new CancellationTokenSource();
if (oldCt != null)
{
oldCt.Cancel();
clt[HashCode] = ct;
}
else
{
clt.Add(HashCode, ct);
}
filterContext.HttpContext.Application["CancellationTokensDictionary"] = clt;
filterContext.Controller.ViewBag.CancellationToken = ct;
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
if (filterContext.Controller.ViewBag.ThreadHasBeenCanceld == null && filterContext.HttpContext.Application["CancellationTokensDictionary"] != null) {
lock (filterContext.HttpContext.Application["CancellationTokensDictionary"])
{
Dictionary<string, CancellationTokenSource> clt = (Dictionary<string, CancellationTokenSource>)filterContext.HttpContext.Application["CancellationTokensDictionary"];
clt.Remove(HashCode);
filterContext.HttpContext.Application["CancellationTokensDictionary"] = clt;
}
}
}
}
I am using AntiForgeryToken here as key token, you can add your own custom hash code to have more control.
In the controller you will have something like this
[HttpPost]
[KeepLastRequest]
public async Task<ActionResult> DoSlowJob(CancellationToken ct)
{
CancellationTokenSource ctv = ViewBag.CancellationToken;
CancellationTokenSource nct = CancellationTokenSource.CreateLinkedTokenSource(ct, ctv.Token, Response.ClientDisconnectedToken);
var mt = Task.Run(() =>
{
SlowJob(nct.Token);
}, nct.Token);
await mt;
return null;
}
private void SlowJob(CancellationToken ct)
{
for (int i = 0; i < 10; i++)
{
Thread.Sleep(200);
if (ct.IsCancellationRequested)
{
this.ViewBag.ThreadHasBeenCanceld = true;
System.Diagnostics.Debug.WriteLine("cancelled!!!");
break;
}
System.Diagnostics.Debug.WriteLine("doing job " + (i + 1));
}
System.Diagnostics.Debug.WriteLine("job done");
return;
}
And finally in your JavaScript you need to abort ongoing requests, otherwise browser blocks new requests.
var onSomethingChanged = function () {
if (currentRequest != null) {
currentRequest.abort();
}
var fullData = $('#my-heavy-form :input').serializeArray();
currentRequest = $.post('/MyController/DoSlowJob', fullData).done(function (data) {
// Do whatever you want with returned data
}).fail(function (f) {
console.log(f);
});
currentRequest.always(function () {
currentRequest = null;
})
}
I am developing ASP.NET Application using c#.net. In that i wrote code for sending a single mail to multiple Mail-Id's .
Hear i used For-loop for continues sending mail.
So, Hear my question is,
1. I want to stop or pause sending mails , when i click "stop" button ???.
2. Is it possible to kill or pause the process of continues sending mails. ???
for (int i = 0; i < B.Length; i++)
{
if (txt_To.Text == "")
{
txt_To.Text = B[i].ToString();
Methord1(); ////////////// UID ,PWD code
int k = i + 1;
Session["num"] = k;
txt_To.Text = "";
Label4.Text = Session["NUM"].ToString() + "Mail sent ...";
}
}
If I am understanding your question correctly you have an emailing process you have started in a thread, and you want to be able to terminate the thread when a stop button is clicked. Is that correct?
The correct way to do this is to create a flag you can set on the threaded class to 'ask' it to terminate - force terminating a thread is a terrible terrible thing.
So, using your existing method I have added a bool in that determines whether the thread keeps executing. You will also need a bool in your class definition that runs all this code:
private volatile bool KeepRunning = true;
public void SendEmails()
{
for (int i = 0; i < B.Length; i++)
{
if (!KeepRunning) return; //<--- this is the new line
if (txt_To.Text == "")
{
txt_To.Text = B[i].ToString();
Methord1(); ////////////// UID ,PWD code
int k = i + 1;
Session["num"] = k;
txt_To.Text = "";
Label4.Text = Session["NUM"].ToString() + "Mail sent ...";
}
}
}
To be able to access the KeepRunning variable it needs to be marked as volatile to indicate you will access it from multiple threads. Now you can invoke the SendEmails() method in a separate thread, and you have a way of asking it to stop later on. T
If that is the case then you will need to retain a reference to the thread you have started the process in:
Thread MyThread = new Thread(new ThreadStart("SendEmails"));
MyThread.Start();
Now the thread is running and looping.
To terminate the thread (in your 'stop' button handler or whatever) you just set KeepRunning as false, and the next time the loop executes it will drop out naturally on that line. You should also wait for the worker thread to rejoin the main thread before continuing:
KeepRunning = false;
MyThread.Join();
Please note this is all example code and hasn't been tested.
In an MVC web application I use the SpeechSynthesizer class to speak some text to a .wav file during a function called by a controller action handler that returns a view. The code executes, writes the file, and the action handle returns, but the development server usually, but not always, never comes back with the return page. This is the text-to-speech code:
string threadMessage = null;
bool returnValue = true;
var t = new System.Threading.Thread(() =>
{
try
{
SpeechEngine.SetOutputToWaveFile(wavFilePath);
SpeechEngine.Speak(text);
SpeechEngine.SetOutputToNull();
}
catch (Exception exception)
{
threadMessage = "Error doing text to speech to file: " + exception.Message;
returnValue = false;
}
});
t.Start();
t.Join();
if (!returnValue)
{
message = threadMessage;
return returnValue;
}
I saw a couple of posts for a similar problem in a service that advised doing the operation in a thread, hence the above thread.
Actually, using the SpeechSynthesizer for other things can hang as well. I had a page that just enumerated the voices, but it would get stuck as well. Since there is no user code in any of the threads if I pause the debugger, I have no clue how to debug it.
I've tried Dispose'ing the SpeechSynthesizer object afterwards, calling SetOutputToDefaultVoice, to no avail. I've tried it on both Windows 8.1 and Windows 8, running with the development server under the debugger, or running IIS Express separately.
Any ideas? Is there other information I could give that would be helpful?
Thanks.
-John
Try
Public void Speak(string wavFilePath, string text)
{
using (var synthesizer = new SpeechSynthesizer())
{
synthesizer.SetOutputToWaveFile(wavFilePath);
synthesizer.Speak(text);
return outputFile;
}
}
Task.Run(() => Speak("path", "text")).Result;
It worked for me in IIS Express
On a webpage, I launche a parallel process to do a long running task then send an email to keep track of its "output".
But the thread sometimes seems to end before sending the email (the email is the only track Ihave) and I think it might be related to a timeout.
Here the code:
var thread = new Thread(() =>
{
var cli = Factory.GetClient(callBack);
FedDBService.ReturnMessage msg;
try
{
msg = cli.SendFedCards(xmls.ToArray());
}
catch (Exception exe)
{
msg = new FedDBService.ReturnMessage();
msg.Error = exe.Message;
}
finally
{
cli.Close();
completion.Finished = true;
}
message = String.Format(#"{0}Ended: {1: HH:mm} {2} {3}", message, DateTime.Now,
msg.Error.Trim(' ', '\n', '\r', '\t') == "" ?
"Withouth errors" : "With errors:"
, msg.Error);
try
{
golf.classes.Mailing.Mail.sendMail(Club.ClubEmail, email, null,
"***#***.com", message, "**Subject**", false);
// this method works right
}
finally
{
Thread.Sleep(5 * 60 * 1000);
RemoveCompletion(guid);
}
});
thread.Start();
Do you think it's related to some timeout? And can I change it withouth allowing usual requests to run forever?
You shouldn't start background work in a web app - if the App Pool is recycled, your background work will be aborted with extreme predjudice.
Phil Haack wrote a blog about this topic here:
The Dangers of Implementing Recurring Background Tasks In ASP.NET
The recommended solution is to pass this work to a seperate Windows Service.