I work on a download system that downloads up to 4 files at once and if there is an error while downloading then it retries up to 5 times.
I would like it to call different callbacks with their variables once it finished downloading.
This is what I wrote:
private void Download(string url, (something) callback)
{
if (asyncworkers++ < Global.paralleldownloads -1) // async download
{
using (WebClient client = new WebClient())
{
client.Proxy = (Global.proxymode == 2 ? new WebProxy(dynamicproxy) : (Global.proxymode == 1 ? new WebProxy(Global.proxy) : null));
System.Timers.Timer timer = new System.Timers.Timer(5000);
timer.Enabled = true;
client.DownloadStringCompleted += (sender, e) =>
{
asyncworkers--;
if (timer.Enabled)
{
timer.Stop();
if (e.Error == null && e.Result.Length > 0)
{
AppendTextBox("successful async\r\n");
errors = 0;
//call "callback" and its variables here
}else{
AppendTextBox("empty async\r\n");
if (errors++ > 3)
{
//stop trying
}else{
Download(url, callback);
}
}
}
};
client.DownloadStringAsync(new Uri(url));
timer.Elapsed += (sender, e) =>
{
AppendTextBox("timeout async\r\n");
timer.Enabled = false;
client.CancelAsync();
Download(url, callback);
};
}
}else{ // sync download to delay it
var request = WebRequest.Create(url);
request.Proxy = (Global.proxymode == 2 ? new WebProxy(dynamicproxy) : (Global.proxymode == 1 ? new WebProxy(Global.proxy) : null));
request.Timeout = 5000;
using (var response = request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
using (var reader = new StreamReader(stream))
{
string data = reader.ReadToEnd();
if (data.Length > 0)
{
AppendTextBox("successful sync\r\n");
asyncworkers--;
errors = 0;
//call "callback" and its variables here
return;
}
}
}
}
asyncworkers--;
AppendTextBox("error sync\r\n");
if (errors++ > 3)
{
//stop trying
}else{
Download(url, callback);
}
}
}
This is how I would like to use it:
Download("http://.....", GetDataDone(var1, var2, var3, var4));
or
Download("http://.....", UpdateDone());
I hope the was I described it is at least a little bit clear to you. How could I get it working the way I wish? Thanks!
Declare callback as Action<T1,T2,T3,T4> where T1 is the type of var1, T2 is var2, etc. Invoke the callback like this:
public void Download(string url, Action<T1,..> callback)
{
//....
callback(var1,var2,var3,var4);
//...
}
Pass a function with matching signature by name as the callback or use a lambda to match the signature:
public void OnDownload(T1 var1, T2 var2, T3 var3, T4 var4)
{
// do stuff here
}
//later
Download("http...", OnDownload);
Update after comment
Use a lambda expression to pass arguments upfront:
public void Download(string url, Action callback)
{
//...
callback();
//..
}
Download("http://..", () => OnDownload(42, "request", user, 0.5f));
Related
In my app i have a list with links.
public List<string> Urls ()
{
var list = new List<string>();
list.Add("http://example.com/image.jpg");
list.Add("http://example.com/image1.jpg");
list.Add("http://example.com/image2.jpg");
return list;
}
I use a webclient for downloading.
private void downloadAlbum_Click(object sender, EventArgs e)
{
foreach (var link in Urls())
{
using (var wc = new WebClient())
{
wc.DownloadFile(link.ToString(),fileName);
}
}
}
But, my winform is lagging. While WebClient do this work.
Maybe anyone have a example for async function with multiple download and show current progress in progress bar?
Update.
I have async await for one file
private void Downloader(string link, string filepath)
{
using (WebClient wc = new WebClient())
{
wc.DownloadProgressChanged += Wc_DownloadProgressChanged;
wc.DownloadFileCompleted += Wc_DownloadFileCompleted;
wc.DownloadFileAsync(new Uri(link), filepath);
}
}
private void Wc_DownloadProgressChanged(object sender,
DownloadProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
private void Wc_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
progressBar.Value = 0;
if (e.Cancelled)
{
MessageBox.Show("Canceled", "Message", MessageBoxButtons.OK,MessageBoxIcon.Error);
return;
}
if (e.Error != null)
{
MessageBox.Show("Somethings wrong, check your internet","Message", MessageBoxButtons.OK,MessageBoxIcon.Error);
return;
}
MessageBox.Show("Download is done!", "Message",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
Progress value:
Tuple<DateTime, long, long> DownloadingProgress = new Tuple<DateTime, long, long>(DateTime.MinValue, 0, 0);
DownloadingProgress = new Tuple<DateTime, long, long>(DateTime.Now, 0, 0);
Before you start to download use this:
Wc.DownloadProgressChanged += DownloadProgressChanged; //when you start download
And here DownloadProgressChanged
private void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs downloadProgressChangedEventArgs)
{
DownloadingProgress = new Tuple<DateTime, long, long>(DateTime.Now, downloadProgressChangedEventArgs.TotalBytesToReceive, downloadProgressChangedEventArgs.BytesReceived);
}
So you can see the progress with percent:
Console.WriteLine("Downloading: " + ((DownloadingProgress.Item3 * 100) / DownloadingProgress.Item2) + "% - " + DownloadingProgress.Item2 + " / " + DownloadingProgress.Item3);
I suggest you to use System.Net.Http.HttpClient instead of WebClient because when you tried to download files asynchronously best practice to use System.Net.Http.HttpClient.
I found idea behind this in link : HttpClientDownload
For downloading list of files you have to use:
private async void DownloadFiles(List<Uri> urls)
{
try
{
Progress<double> progress = new Progress<double>();
foreach (Uri uri in urls)
{
if (!client.isProcessCancel)
{
//Gets download progress - pgrBarDowload is our Progress Bar
progress.ProgressChanged += (sender, value) => pgrBarDowload.Value = (int)value;
}
var cancellationToken = new CancellationTokenSource();
writeOperation("Downloading File: " + uri.OriginalString);
//Set files in download queue
client.isProcessCancel = false;
await client.DownloadFileAsync(uri.OriginalString, progress, cancellationToken.Token, directoryPath);
}
}
catch (Exception ex)
{
writeOperation(ex.Message);
}
}
This method will download provided files asynchronously:
HttpClient httpClient = new HttpClient();
httpClient.Timeout = TimeSpan.FromMinutes(30);
public async Task DownloadFileAsync(string url, IProgress<double> progress, CancellationToken token, string fileDirectoryPath)
{
using (HttpResponseMessage response = httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead).Result)
{
response.EnsureSuccessStatusCode();
//Get total content length
var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L;
var canReportProgress = total != -1 && progress != null;
using (Stream contentStream = await response.Content.ReadAsStreamAsync(), fileStream = new FileStream(fileDirectoryPath + url.Substring(url.LastIndexOf('/') + 1), FileMode.Create, FileAccess.Write, FileShare.ReadWrite, 8192, true))
{
var totalRead = 0L;
var totalReads = 0L;
var buffer = new byte[8192];
var isMoreToRead = true;
do
{
var read = await contentStream.ReadAsync(buffer, 0, buffer.Length);
if (read == 0)
{
isMoreToRead = false;
}
else
{
await fileStream.WriteAsync(buffer, 0, read);
totalRead += read;
totalReads += 1;
if (totalReads % 2000 == 0 || canReportProgress)
{
//Check if operation is cancelled by user
if (!isProcessCancel)
{
progress.Report((totalRead * 1d) / (total * 1d) * 100);
}
else
{
progress.Report(100);
}
}
}
}
while (isMoreToRead);
}
}
}
Note: Progress Bar will show progress for each file individually. This code is tested for Large Media files too. You can also set Progress for all files together by Calculating files size before download start.
I have created small demonstration in c# WinForm application related to Files download, Download progress and File Operations. Please follow below link: enter link description here
Please feel free comment any suggestions or doubts.
The WPF code below hangs forever when network connection is lost for 3 or more minutes. When connection is restored it neither throws nor continues downloading nor timeouts. If network connection is lost for a shorter period say half a minute, it throws after connection is restored. How can i make it more robust to survive network outage?
using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Windows;
namespace WebClientAsync
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
NetworkChange.NetworkAvailabilityChanged +=
(sender, e) => Dispatcher.Invoke(delegate()
{
this.Title = "Network is " + (e.IsAvailable ? " available" : "down");
});
}
const string SRC = "http://ovh.net/files/10Mio.dat";
const string TARGET = #"d:\stuff\10Mio.dat";
private async void btnDownload_Click(object sender, RoutedEventArgs e)
{
btnDownload.IsEnabled = false;
btnDownload.Content = "Downloading " + SRC;
try {
using (var wcl = new WebClient())
{
wcl.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
await wcl.DownloadFileTaskAsync(new Uri(SRC), TARGET);
btnDownload.Content = "Downloaded";
}
}
catch (Exception ex)
{
btnDownload.Content = ex.Message + Environment.NewLine
+ ((ex.InnerException != null) ? ex.InnerException.Message : String.Empty);
}
btnDownload.IsEnabled = true;
}
}
}
UPDATE
Current solution is based on restarting Timer in DownloadProgressChangedEventHandler, so the timer fires only if no DownloadProgressChanged events occur within the timeout. Looks like an ugly hack, still looking for a better solution.
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace WebClientAsync
{
public partial class MainWindow : Window
{
const string SRC = "http://ovh.net/files/10Mio.dat";
const string TARGET = #"d:\stuff\10Mio.dat";
// Time needed to restore network connection
const int TIMEOUT = 30 * 1000;
public MainWindow()
{
InitializeComponent();
}
private async void btnDownload_Click(object sender, RoutedEventArgs e)
{
btnDownload.IsEnabled = false;
btnDownload.Content = "Downloading " + SRC;
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
Timer timer = new Timer((o) =>
{
// Force async cancellation
cts.Cancel();
}
, null //state
, TIMEOUT
, Timeout.Infinite // once
);
DownloadProgressChangedEventHandler handler = (sa, ea) =>
{
// Restart timer
if (ea.BytesReceived < ea.TotalBytesToReceive && timer != null)
{
timer.Change(TIMEOUT, Timeout.Infinite);
}
};
btnDownload.Content = await DownloadFileTA(token, handler);
// Note ProgressCallback will fire once again after awaited.
timer.Dispose();
btnDownload.IsEnabled = true;
}
private async Task<string> DownloadFileTA(CancellationToken token, DownloadProgressChangedEventHandler handler)
{
string res = null;
WebClient wcl = new WebClient();
wcl.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
wcl.DownloadProgressChanged += handler;
try
{
using (token.Register(() => wcl.CancelAsync()))
{
await wcl.DownloadFileTaskAsync(new Uri(SRC), TARGET);
}
res = "Downloaded";
}
catch (Exception ex)
{
res = ex.Message + Environment.NewLine
+ ((ex.InnerException != null) ? ex.InnerException.Message : String.Empty);
}
wcl.Dispose();
return res;
}
}
}
You need to implement proper timeout for that download. But you don't need to use timer, just use Task.Delay and Task.WaitAny. For example:
static async Task DownloadFile(string url, string output, TimeSpan timeout) {
using (var wcl = new WebClient())
{
wcl.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
var download = wcl.DownloadFileTaskAsync(url, output);
// await two tasks - download and delay, whichever completes first
await Task.WhenAny(Task.Delay(timeout), download);
var exception = download.Exception; // need to observe exception, if any
bool cancelled = !download.IsCompleted && exception == null;
// download is not completed yet, nor it is failed - cancel
if (cancelled) {
wcl.CancelAsync();
}
if (cancelled || exception != null) {
// delete partially downloaded file if any (note - need to do with retry, might not work with a first try, because CancelAsync is not immediate)
int fails = 0;
while (true) {
try {
File.Delete(output);
break;
}
catch {
fails++;
if (fails >= 10)
break;
await Task.Delay(1000);
}
}
}
if (exception != null) {
throw new Exception("Failed to download file", exception);
}
if (cancelled) {
throw new Exception($"Failed to download file (timeout reached: {timeout})");
}
}
}
Usage:
const string SRC = "http://ovh.net/files/10Mio.dat";
const string TARGET = #"d:\stuff\10Mio.dat";
// Time needed to restore network connection
TimeSpam TIMEOUT = TimeSpan.FromSeconds(30);
DownloadFile(SRC,TARGET, TIMEOUT); // might want to await this to handle exceptions
Update in response to comment. If you want timeout based on received data, not on whole operation time, it's also possible with Task.Delay. For example:
static async Task DownloadFile(string url, string output, TimeSpan timeout)
{
using (var wcl = new WebClient())
{
wcl.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
DateTime? lastReceived = null;
wcl.DownloadProgressChanged += (o, e) =>
{
lastReceived = DateTime.Now;
};
var download = wcl.DownloadFileTaskAsync(url, output);
// await two tasks - download and delay, whichever completes first
// do that until download fails, completes, or timeout expires
while (lastReceived == null || DateTime.Now - lastReceived < timeout) {
await Task.WhenAny(Task.Delay(1000), download); // you can replace 1 second with more reasonable value
if (download.IsCompleted || download.IsCanceled || download.Exception != null)
break;
}
var exception = download.Exception; // need to observe exception, if any
bool cancelled = !download.IsCompleted && exception == null;
// download is not completed yet, nor it is failed - cancel
if (cancelled)
{
wcl.CancelAsync();
}
if (cancelled || exception != null)
{
// delete partially downloaded file if any (note - need to do with retry, might not work with a first try, because CancelAsync is not immediate)
int fails = 0;
while (true)
{
try
{
File.Delete(output);
break;
}
catch
{
fails++;
if (fails >= 10)
break;
await Task.Delay(1000);
}
}
}
if (exception != null)
{
throw new Exception("Failed to download file", exception);
}
if (cancelled)
{
throw new Exception($"Failed to download file (timeout reached: {timeout})");
}
}
}
Personally, if I were to make a robust download solution, I would add a Network connection monitor because that's what we are actually waiting for. For simplicity, something like this will be enough.
online = true;
NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
_isNetworkOnline = NetworkInterface.GetIsNetworkAvailable();
void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
online = e.IsAvailable;
}
Then you can actually check for network availability and wait as appropriate before you attempt to download or progress... I will definitely accept that a simple ping solution seems to work better than this at times based on experience.
Depending on the size of what you're downloading, monitoring the network speed may also help so you can decide how to chunk in case of choppy connections. Take a look at this project for ideas.
So I thought Webclient.DownloadFileAysnc would have a default timeout but looking around the documentation I cannot find anything about it anywhere so I'm guessing it doesn't.
I am trying to download a file from the internet like so:
using (WebClient wc = new WebClient())
{
wc.DownloadProgressChanged += ((sender, args) =>
{
IndividualProgress = args.ProgressPercentage;
});
wc.DownloadFileCompleted += ((sender, args) =>
{
if (args.Error == null)
{
if (!args.Cancelled)
{
File.Move(filePath, Path.ChangeExtension(filePath, ".jpg"));
}
mr.Set();
}
else
{
ex = args.Error;
mr.Set();
}
});
wc.DownloadFileAsync(new Uri("MyInternetFile", filePath);
mr.WaitOne();
if (ex != null)
{
throw ex;
}
}
But if I turn off my WiFi (simulating a drop of internet connection) my application just pauses and the download stops but it will never report that through to the DownloadFileCompleted method.
For this reason I would like to implement a timeout on my WebClient.DownloadFileAsync method. Is this possible?
As an aside I am using .Net 4 and don't want to add references to third party libraries so cannot use the Async/Await keywords
You can use WebClient.DownloadFileAsync(). Now inside a timer you can call CancelAsync() like so:
System.Timers.Timer aTimer = new System.Timers.Timer();
System.Timers.ElapsedEventHandler handler = null;
handler = ((sender, args)
=>
{
aTimer.Elapsed -= handler;
wc.CancelAsync();
});
aTimer.Elapsed += handler;
aTimer.Interval = 100000;
aTimer.Enabled = true;
Else create your own weclient
public class NewWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
var req = base.GetWebRequest(address);
req.Timeout = 18000;
return req;
}
}
Create a WebClientAsync class that implements the timer in the constructor. This way you aren't copying and pasting the timer code into every implementation.
public class WebClientAsync : WebClient
{
private int _timeoutMilliseconds;
public EdmapWebClientAsync(int timeoutSeconds)
{
_timeoutMilliseconds = timeoutSeconds * 1000;
Timer timer = new Timer(_timeoutMilliseconds);
ElapsedEventHandler handler = null;
handler = ((sender, args) =>
{
timer.Elapsed -= handler;
this.CancelAsync();
});
timer.Elapsed += handler;
timer.Enabled = true;
}
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
request.Timeout = _timeoutMilliseconds;
((HttpWebRequest)request).ReadWriteTimeout = _timeoutMilliseconds;
return request;
}
protected override voidOnDownloadProgressChanged(DownloadProgressChangedEventArgs e)
{
base.OnDownloadProgressChanged(e);
timer.Reset(); //If this does not work try below
timer.Start();
}
}
This will allow you to timeout if you lose Internet connection while downloading a file.
Here is another implementation, I tried to avoid any shared class/object variables to avoid trouble with multiple calls:
public Task<string> DownloadFile(Uri url)
{
var tcs = new TaskCompletionSource<string>();
Task.Run(async () =>
{
bool hasProgresChanged = false;
var timer = new Timer(new TimeSpan(0, 0, 20).TotalMilliseconds);
var client = new WebClient();
void downloadHandler(object s, DownloadProgressChangedEventArgs e) => hasProgresChanged = true;
void timerHandler(object s, ElapsedEventArgs e)
{
timer.Stop();
if (hasProgresChanged)
{
timer.Start();
hasProgresChanged = false;
}
else
{
CleanResources();
tcs.TrySetException(new TimeoutException("Download timedout"));
}
}
void CleanResources()
{
client.DownloadProgressChanged -= downloadHandler;
client.Dispose();
timer.Elapsed -= timerHandler;
timer.Dispose();
}
string filePath = Path.Combine(Path.GetTempPath(), Path.GetFileName(url.ToString()));
try
{
client.DownloadProgressChanged += downloadHandler;
timer.Elapsed += timerHandler;
timer.Start();
await client.DownloadFileTaskAsync(url, filePath);
}
catch (Exception e)
{
tcs.TrySetException(e);
}
finally
{
CleanResources();
}
return tcs.TrySetResult(filePath);
});
return tcs.Task;
}
I have an app with a start button that calls a long running time function. In order
to add a Stop button I've added a thread for this function to avoid the UI freezes and be able to stop the processing in anytime.
The code without threading in average takes 12 minutes to complete the processing, but with threading in the way I have below
takes 4 times more. Below is shown the code for the start button where is called the function "LongRunningFunction" . The function
needs a string argument to work "LongRunningFunction(Somestring)".
I've tested with Task.Run and Task.Factory.StartNew but it happens the same with both methods.
Is there an alternative way to set a thread for my case that doesn't affect too much the performance?
public partial class Form1 : Form
{
CancellationTokenSource cts = new CancellationTokenSource(); // Create the token source.
public Form1()
{
InitializeComponent();
}
private void Start_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
cts = new CancellationTokenSource();
Task.Run(()=> LongRunningFunction(Somestring, cts.Token), cts.Token);
//Task.Factory.StartNew(() => LongRunningFunction(Somestring, cts.Token), cts.Token, TaskCreationOptions.None, TaskScheduler.Default);
}
private void Stop_Click(object sender, EventArgs e)
{
if (cts != null)
{
cts.Cancel();
cts = null;
MessageBox.Show("Processing cancelled");
}
}
public void LongRunningFunction(string String, CancellationToken token)
{
//Long running processing
//...
MessageBox.Show("Processing finished");
}
}
Update:
The only what I changed is the way I declare the function and added an if statement inside the while loop
that is inside the function. Is like below:
The CancelationToken was added in order to be able to stop the processing when Stop button is pressed.
Without thread I declare the function like this:
public void LongRunningFunction(string String)
{
while (condition)
{
//My code within While loop
}
MessageBox.Show("Processing finished");
}
and with Thread I define the function like this:
public void LongRunningFunction(string String, CancellationToken token)
{
while (condition)
{
if (token.IsCancellationRequested)
{
break;
}
//My code within While loop
}
if (!token.IsCancellationRequested)
{
MessageBox.Show("Processing finished");
}
}
Update2:
Inside LongRunningFunction() is called another function that prints the lines. Is like below.
public void LongRunningFunction(string fileName, CancellationToken token)
{
StreamWriter writer = new StreamWriter(#outputfile, true, Encoding.UTF8, 4096);
using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
List<byte> buffer = new List<byte>();
List<string> buffer1 = new List<string>();
SoapHexBinary hex = new SoapHexBinary();
while (chunk.Length > 0)
{
if (token.IsCancellationRequested) // ### For Cancel Thread ###
{
break;
} // ### For Cancel Thread ###
chunk = reader.ReadBytes(1024);
foreach (byte data in chunk)
{
if (somecondition)
{
buffer.Add(data);
}
else if (other condition)
{
buffer.Add(data);
PrintFunction(buffer, hex, outputfile, writer); // Print Line
}
else if (some other condition)
{
buffer.Add(data);
}
}
}
if (!token.IsCancellationRequested)
{
MessageBox.Show("Processing finished");
}
}
if (writer != null)
{
writer.Dispose();
writer.Close();
}
}
private void PrintFunction(List<byte> buffer, SoapHexBinary hex, string outputfile, StreamWriter writer)
{
if (buffer.Count > 0)
{
if (buffer.Count >= lowlimit)
{
hex.Value = buffer.ToArray();
string Register = hex.ToString();
Regex pattern1 = new Regex(#"some pattern");
if (pattern1.IsMatch(Register))
{
Match l1 = Regex.Match(Register, #"somepattern", RegexOptions.IgnoreCase | RegexOptions.Compiled);
writer.Write("{0}|{1}|{2}", Convert.ToInt32(l1.Groups[1].ToString(), 16), l1.Groups[2].Value, l1.Groups[3].Value);
Match l2 = Regex.Match(Register, #"otherpattern", RegexOptions.IgnoreCase | RegexOptions.Compiled);
if (l2.Success)
{
foreach (Match m in Regex.Matches(l2.Groups[2].ToString(), pattern2, RegexOptions.IgnoreCase | RegexOptions.Compiled))
{
//Some foreach code
}
foreach (Match x in Regex.Matches(var, #"pattern"))
{
//come code
}
writer.WriteLine("," + String.Join(",", var1));
}
else
{
writer.WriteLine();
}
}
}
}
buffer.Clear();
}
Update3:
Hi bebosh,
I still have doubts how to apply in my function, the way you define the delegate in your example function.
My function looks like this:
public void LongRunningFunction(string fileName)
{
using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
// some code
}
}
It could be something like this or how?:
private void LongRunningFunction(string fileName)
{
MethodInvoker action = delegate
{
using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
{
// some code
}
};
}
Bebosh's answer was good enough. To increase the performance further you can set the ThreadPriority of the "thread" by setting the ".Priority = ThreadPriority.AboveNormal" right after setting the "thread.IsBackground = true;".
Could you try this code:
bool Stop = false;
Thread thread;
private void StartButton_Click(object sender, EventArgs e)
{
string FileName = #"...\a.bin";
thread = new Thread(new ThreadStart(() => DoLongProcess(FileName)));
thread.IsBackground = true;
thread.Start();
}
private void StopButton_Click(object sender, EventArgs e)
{
Stop = true;
}
private void DoLongProcess(string file)
{
using (BinaryReader reader = new BinaryReader(File.Open(file, FileMode.Open)))
{
int pos = 0;
int length = (int)reader.BaseStream.Length;
while (pos < length)
{
if (Stop)
thread.Abort();
// using Invoke if you want cross UI objects
this.Invoke((MethodInvoker)delegate
{
label1.Text = pos.ToString();
});
pos += sizeof(int);
}
}
}
use interrupting the thread
Thread thread;
public MainWindow()
{
InitializeComponent();
}
private async void StartButtonClick(object sender, RoutedEventArgs e)
{
thread = new Thread(ExecuteLong);
var task = Task.Run(() =>
thread.Start());
await task;
}
private void ExecuteLong()
{
try
{
// long task
}
catch (ThreadInterruptedException e)
{
MessageBox.Show("cancelled!");
return;
}
MessageBox.Show("finished");
}
private void CancelButtonClick(object sender, RoutedEventArgs e)
{
this.thread.Interrupt();
}
First post here, sorry for starting with asking questions.
In my Windows Phone 7 app I have a working livetile that is beeing triggered by a background agent. But how can I modify the code so the httpwebrequest timeouts after 10 seconds?
Thanks in advance.
protected override void OnInvoke(ScheduledTask task)
{
//TODO: Add code to perform your task in background
var request = (HttpWebRequest)WebRequest.Create(
new Uri("site.com"));
request.BeginGetResponse(r =>
{
var httpRequest = (HttpWebRequest)r.AsyncState;
var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
using (var reader = new StreamReader(httpResponse.GetResponseStream()))
{
var response = reader.ReadToEnd();
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
string strResult = response;
/// If application uses both PeriodicTask and ResourceIntensiveTask
if (task is PeriodicTask)
{
// Execute periodic task actions here.
ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("TileID=2"));
if (TileToFind != null)
{
StandardTileData NewTileData = new StandardTileData
{
BackgroundImage = new Uri("Pin-to-start.png", UriKind.Relative),
Title = strResult,
Count = null
};
TileToFind.Update(NewTileData);
}
}
else
{
// Execute resource-intensive task actions here.
}
NotifyComplete();
}));
}
}, request);
}
Here is copy/paste from code that i use in one of my apps. It will abort the connection after 60 seconds.
private static void DoWebRequest(string uri)
{
string id = "my_request";
Timer t = null;
int timeout = 60; // in seconds
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Accept = "*/*";
request.AllowAutoRedirect = true;
// disable caching.
request.Headers["Cache-Control"] = "no-cache";
request.Headers["Pragma"] = "no-cache";
t = new Timer(
state =>
{
if (string.Compare(state.ToString(), id, StringComparison.InvariantCultureIgnoreCase) == 0)
{
logger.Write("Timeout reached for connection [{0}], aborting download.", id);
request.Abort();
t.Dispose();
}
},
id,
timeout * 1000,
0);
request.BeginGetResponse(
r =>
{
try
{
if (t != null)
{
t.Dispose();
}
// your code for processing the results
}
catch
{
// error handling.
}
},
request);
}
catch
{
}
}
But how can I modify the code so the httpwebrequest timeouts after 10 seconds?
You mean so it'll call NotifyComplete() regardless of timeouts?-) The catch is that after 15 seconds the task terminates, and gets disabled until it's re-launched by the user (inside your app).
I would recommend using TPL for Silverlight and utilizing the ability to use Tasks for setting a Timeout.
Something like:
protected override void OnInvoke(ScheduledTask task)
{
var fetchTask = FetchData(TimeSpan.FromSeconds(10));
fetchTask.ContinueWith(x =>
{
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
string strResult = x.Result; // mind you, x.Result will be "null" when a timeout occours.
...
NotifyComplete();
}));
});
}
private Task<string> FetchData(TimeSpan timeout)
{
var tcs = new TaskCompletionSource<string>();
var request = (HttpWebRequest)WebRequest.Create(new Uri("site.com"));
Timer timer = null;
timer = new Timer(sender =>
{
tcs.TrySetResult(null);
timer.Dispose();
}, null, (int)timeout.TotalMilliseconds, Timeout.Infinite);
request.BeginGetResponse(r =>
{
var httpRequest = (HttpWebRequest)r.AsyncState;
var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
using (var reader = new StreamReader(httpResponse.GetResponseStream()))
{
var response = reader.ReadToEnd();
tcs.TrySetResult(response);
}
});
return tcs.Task;
}