I've made a simple program that has to continuosly check for data based on API.
So far, what I've done is making a timer, then execute the GET procedures on timer event
private void TimerStatus_Tick(object sender, EventArgs e)
{
//stop timer
TimerStatus.Stop();
//get data
getCommand();
//restart timer
TimerStatus.Start();
}
void getCommand()
{
string url = "https://somewhere/getcommand?token=somekey¶m=";
string param = "0";
WebRequest request = WebRequest.Create(url + param ); ;
request.Method = "GET";
request.ContentType = "application/x-www-form-urlencoded";
request.Credentials = CredentialCache.DefaultCredentials;
try
{
WebResponse response = request.GetResponse();
bool connected = false;
if ((((HttpWebResponse)response).StatusDescription) == "OK")
connected = true;
//continue if connected
if (connected)
{
using (Stream dataStream = response.GetResponseStream())
{
// Open the stream using a StreamReader for easy access.
StreamReader reader = new StreamReader(dataStream);
// Read the content.
string responseFromServer = reader.ReadToEnd();
//check output
Console.WriteLine("Respond from server : " + responseFromServer);
try
{
//parse data, store value
parseThenProcess(responseFromServer);
}
catch
{
//parsing data error
Console.WriteLine("exception error response");
}
}
}
// Close the response.
response.Close();
}
catch
{
Console.WriteLine("Get command failed");
}
}
This code works fine for me. However, when I try to add more command that has different API in the timer event, the winforms feels kinda laggy. Is it just error on my side that irrelevant with the API handling or do I need to make some improvement about how to handle the API?
private void TimerStatus_Tick(object sender, EventArgs e)
{
//stop timer
TimerStatus.Stop();
//get data
getCommand_A();
getCommand_B();
getParameter_C();
getParameter_D();
//restart timer
TimerStatus.Start();
}
Not using a windows timer? And I am not joking. You have various approaches:
Learn how to use async and the async web interfaces so you do not block the UI thread too long.
or
use a separate thread or tasks (no need for a timer , you can have a task that then schedules another task, i.e.).
What you do is running it all on the UI thread and that is really not needed. Especially because you do send that synchronous so the UI blocks while the request is executed .This is a problem solved for many years by the UI knowing of async methods.
Related
I am building an Android app through Xamarin/MonoTouch (so using C# rather than Java) and would like to display a ProgressBar while the app is communicating with the server. I have been attempting to do this using async, but I don't have a good understanding of how threading works, and I've been running into the same issue for the past few hours - the ProgressBar shows after my method call rather than before. My code is in the OnCreate() method, which I have also overridden to be async. Here it is:
loginButton.Click += async (sender, e) =>
{
progbar.Visibility = ViewStates.Visible;
var userFetcher = new UserFetcher();
var json = await userFetcher.FetchUserDetailsAsync(/*parameters*/);
//the above method is async and returns a Task<JsonValue>
ParseUserDetails(json); //another method I created
progbar.Visibility = ViewStates.Invisible;
//do some stuff using the parsed data
};
The issue I'm running into is that the FetchUserDetailsAsync seems to be blocking my thread. I have been testing this by shutting the server off so that it takes a long response time (but even when I test stuff like Thread.Sleep(5000) I have the same issue). After the method has been called, it runs both progbar.Visibility = ViewStates.Visible; and progbar.Visibility = ViewStates.Invisible; right after one another - I know this because when I comment out the Invisible part, the ProgressBar appears after my method got a "response" from the server. The compiler has also been giving me messages like "Skipped 67 frames! The application may be doing too much work on its main thread."
Like I said earlier, I'm not really experienced with threading, so it's very possible I'm just naively doing something wrong. Does anyone have a solution to this issue?
EDIT: Here is the source code for FetchUserDetailsAsync:
public async Task<JsonValue> FetchUserDetailsAsync(string url, string username, string password)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri(url));
request.ContentType = "application/json";
request.Method = "GET";
string myBasicHeader = AuthenticationHelper.MakeHeader(username, password);
request.Headers.Add("Authorization", "Basic " + myBasicHeader);
try
{
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
JsonValue jsonDoc = await Task.Run(() => JsonObject.Load(stream));
return jsonDoc;
}
}
}
catch (WebException e)
{
Console.WriteLine(e.ToString());
if (e.Status == WebExceptionStatus.ProtocolError)
{
var response = e.Response as HttpWebResponse;
if (response != null)
{
Console.WriteLine("HTTP Status Code: " + (int)response.StatusCode);
}
else
{
Console.WriteLine("No http status code available");
}
}
return null;
}
}
It looks like either FetchUserDetailsAsync or ParseUserDetails is blocking the UI thread. Because Task.Sleep(5000) is running synchronously. Try await Task.Delay(5000); in the line of FetchUserDetailsAsync to see if the progressbar show up. If it does, then you probably need to go into FetchUserDetailsAsync implementation to make sure it is implemented async
I figured out the issue. I was calling GetResponse() instead of GetResponseAsync() in my FetchUserDetailsAsync method.
I have a class that I created to consume a REST API. I wrote the class to communicate asynchronously with the web service, since I didn't originally think I needed to have anything run synchronized. Now I am experiencing a situation where I realized using an asynchronous method is not ideal for one particular situation in my application since it runs out of order and causes exceptions since the application is attempting to call a method that it is not ready for. I'm not 100% sure why this is happening, but I think it's due to these methods being called in async void events within my UI. Here are some code snippets that show an example of the situation:
class MyForm : Form
{
private RestConnection connection;
private async void MyForm_Load(object sender, EventArgs e)
{
if(connection == null)
using (LogOnDialog logOnDialog = new LogOnDialog())
{
var result = logOnDialog.ShowDialog(this);
if(result == DialogResult.OK)
{
connection = logOnDialog.Connection;
}
}
formComboBox.DataSource = await connection.GetChoices();
}
}
class LogOnDialog : Form
{
public RestConnection Connection {private set;get;}
private async void saveButton_Click(object sender, EventArgs e)
{
RestConnection conn = new RestConnection(userNameTB.Text, passwordTb.Text);
await conn.LogIn();
if(conn.LoggedIn) //issue here
{
Connection = conn;
DialogResult = DialogResult.OK;
this.Close();
}
else
{
Connection = null;
DialogResult = DialogResult.Abort;
MessageBox.Show("Invalid Credentials, Try Again.");
}
}
}
What's happening is that the application is attempting to call connection.GetOptions(), but connection is still null because the LogOnDialog's async event that creates the connection and check's for a successful login before allowing the connection to be offered to the caller. However, since connection is null since the Click event hasn't completed a NullReferenceException is called. Additionally, if I continue past and ignore the exception an ObjectDisposedException is thrown since we're now outside of the using block.
I attempted to force the logon to be synchronous by removing the async keyword from the event, and calling Wait() on the login Method. This caused a deadlock. I also tried to capture the task using the below code, and spinwait for it:
Task t = conn.LogOn();
while(!t.IsCompleted)
Thread.Sleep(50);
This didn't deadlock, but it did spin forever. Every time I checked the breakpoint on the While condition the Task's status was always WAITINGFORACTIVATION and essentially locked up the application. In order to get this working I'm going to create some synchronous methods for this situation, but what would allow this to work properly and be async all the way?
EDIT: Additional Code Snippet as Requested for LogOn() and GetOptions()
class RestConnection
{
private string user;
private string password
private XDocument convertToXDoc(string functionName, IDictionary<string,string> parameters) {} //not shown, but this just creates an XML document in the required format for the REST service to consume.
private async Task<XDocument> SendCommand(XDocument commandDocument)
{
XDocument responseData = null;
byte[] data = Encoding.UTF8.GetBytes(commandDoc.ToString());
HttpWebRequest request = WebRequest.CreateHttp(this.serverUrl);
request.Method = "POST";
request.ContentType = "text/xml";
request.ContentLength = data.Length;
using (var requestStream = await request.GetRequestStreamAsync())
{
await requestStream.WriteAsync(data, 0, data.Length);
}
HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse;
using (var responseStream = response.GetResponseStream())
{
responseData = XDocument.Load(responseStream);
}
return responseData;
}
public async Task LogIn()
{
var parameters = new Dictionary<string, string>();
parameters.Add("USERNAME", userName);
parameters.Add("PASSWORD", passWord);
parameters.Add("CORELICTYPE", String.Empty);
parameters.Add("REMOTEAUTH", "False");
var xmlCommand = ConvertMethodToXml("LoginUserEx3", parameters);
var response = await SendCommand(xmlCommand);
//read response
switch (response.Root.Element("RESULTS").Element("RESULTVAL").Value)
{
case "0":
sessionId = response.Root.Element("SESSIONID").Value;
pingRequired = response.Root.Element("PINGTIME").Value != "0";
if (pingRequired)
{
pingInterval = int.Parse(response.Root.Element("PINGTIME").Value);
pingTimer = new Timer(pingInterval);
pingTimer.Elapsed += PingServerRequired;
pingTimer.Start();
}
loggedIn = true;
break;
//removed other cases for example since they all throw exceptions
default:
loggedIn = false;
throw new ConnectionException("Error");
}
}
}
The GetOptions() in the same format as the LogIn() method, except it returns a Task<List<Options>> from parsing the returned XDocument.
The problem is here:
{
Connection = null;
DialogResult = DialogResult.Abort; //<<------ this
MessageBox.Show("Invalid Credentials, Try Again.");
}
Assigning DialogResult will automatically close your form with the result you pass in. Remove that line and you will be fine (especially if you want the dialog to never close).
I have a WCF service which accepts requests and for each request makes an HTTPWebRequest call and returns the response. I use a BlockingCollection to store the requests as they come in and a separate thread processes (makes the webrequest) the items in the collection. Sometimes the Webrequest returns a threadabortexception. I catch it and do a Thread.ResetAbort. But the exception flows up and and it clears the BlockingCollection. I have added snippets of the code below. I need to find a way for the foreach loop to keep continuing even if i get a threadabort exception.
public static class YBProcessor
{
static PriorityQueue<QueuePriorityLevel, string> queue = new PriorityQueue<QueuePriorityLevel, string>();
static BlockingCollection<KeyValuePair<QueuePriorityLevel, string>> requests;
static YBProcessor()
{
requests = new BlockingCollection<KeyValuePair<QueuePriorityLevel, string>>(queue);
Task.Factory.StartNew(() => SendRequestToYB());
}
public static void AddCalcRequest(string cusip, double price, QueuePriorityLevel priority)
{
requests.Add(new KeyValuePair<QueuePriorityLevel, string>(priority, cusip + "-" + price.ToString()));
}
private static void SendRequestToYB()
{
// this is a separate thread that processes the requests as the come in.
foreach (var obj in requests.GetConsumingEnumerable())
{
try
{
var request = GetXML(obj.Value);
var response = YBClient.GetResponse(request);
//code to handle response
}
catch (ThreadAbortException ex)
{
Thread.ResetAbort();
}
catch (Exception ex)
{
}
}
}
}
// In YBClient The GetResponse Method (just the key parts. Code wont compile)
private static String GetResponse(String text)
{
for (iTry = 0; iTry < MAX_TRY; iTry++)
{
try
{
// Create and setup request
bytes = encoding.GetBytes(text);
request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "POST";
request.ContentType = "text/xml";
request.ContentLength = bytes.Length;
request.CookieContainer = cookieJar;
request.Timeout = 100 * 1000;
request.ReadWriteTimeout = 100 * 1000;
// Prepare and send data
postStream = request.GetRequestStream();
postStream.Write(bytes, 0, bytes.Length);
postStream.Close();
// Get response from server
response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode == HttpStatusCode.OK)
{
reader = new StreamReader(response.GetResponseStream(), encoding);
xmlResponse = reader.ReadToEnd();
reader.Close();
break;
}
response.Close();
}
catch (ThreadAbortException ex)
{
Thread.ResetAbort();
break;
}
catch (Exception ex)
{
if (ex.GetBaseException() is ThreadAbortException)
{
Thread.ResetAbort();
break;
}
}
}
}
return xmlResponse;
}
If the service itself is calling the thread abort exception then it might mean the service is going down, in that scenario your thread cannot continue to function (imagine a app pool recycle), If you want to make sure your pending requests are not lost then you can do one of the following:
1) Start a new appdomain http://msdn.microsoft.com/en-us/library/ms173139(v=vs.90).aspx
This method will mitigate service shutting down, however will still not resolve the fundamental issue of making sure all your requests are processed as the "other" app domain can also go down.
2) The better solution will be to keep writing your requests in serialized form in a central DB or a file and get a worker to keep popping items out of the same, if you want simplicity, create seperate files for each requests and delete them once processed (assuming you wont get thousands of requests/sec), for a more scalable solutions you can use Redis database (http://redis.io/) and use its "list" (queue) functionality.
P.S. You might want to mark your thread (task) as long running, if you don't, it uses the thread pool which is not recommended for very long running tasks.
Task.Factory.StartNew(Run, TaskCreationOptions.LongRunning);
I am working on Visual Web Express 2010.
I am trying to upload a file to server and block the calling function and release it once uploading is complete. However, the main thread never gets unblocked.
public partial class MainPage : UserControl
{
private FileStream fileStream;
private static String responseStr = "";
private static ManualResetEvent evt = new ManualResetEvent(false);
public MainPage()
{
InitializeComponent();
HtmlPage.RegisterScriptableObject("App", this);
}
public void sendPhoto()
{
uploadFile();
}
private void uploadFile()
{
uploadDataToServer(url);
evt.WaitOne();
postProcess();
}
public static void postProcess()
{
HtmlPage.Window.Invoke("postProcess2", responseStr);
}
void uploadDataToServer(String url)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = "multipart/form-data; boundary=---------------------------" + _boundaryNo;
request.Method = "POST";
request.BeginGetRequestStream(writeCallback, request);
}
private void writeCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
MemoryStream memoryStream = new MemoryStream();
fileStream.CopyTo(memoryStream);
if (memoryStream != null)
{
memoryStream.Position = 0;
byte[] img = memoryStream.ToArray();
Stream postStream = request.EndGetRequestStream(asynchronousResult);
postStream.Write(img, 0, img.Length);
request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
}
}
private void GetResponseCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
streamRead.Close();
streamResponse.Close();
response.Close();
responseStr = responseString;
evt.Set();
}
}
Now, when I use evt.WaitOne() in uploadFile, the whole app goes waiting and no request is send to server, i.e. the code never reaches getResponseCallBack and hence the application never wakes up.
However, if I don't use evt.WaitOne(), then the request is successful, however I can't read the response text since it's set in writeCallBack() function and the request is async one. What should I do to get over this problem?
I can't figure out:
1. If the request is multi-threaded / Async, then why evt.WaitOne() makes the complete app waiting and the request doesn't complete?
2. If the request is single threaded, then why postProcess() [removing the evt.WaitOne()] on trying to access responseStr [set in the getResponseCallBack()] doesn't get the proper response set in it.
[Sorry, I am new to this and am confused].
Thanks.
Sorry, I forgot to mention one thing that, I am using silverlight.
There is no point in starting an async operation and then waiting for it to complete. Instead of async request.BeginGetRequestStream(writeCallback, request); use the synchronous request.GetRequestStream(...) and consume it's result. ManualResetEvent would not be needed then.
Here is the solution to the problem that I figured out:
Some have advised to use HttpWebRequest.GetRequestStream and make the call synchronous. However, as it seems that silverlight doesn't allow this to take place. Moreover, there was many confusion related to threading and parrallel-processing.
The main problem was that I wasn't able to access the UI thread from my callback function, so I used the dispatcher method and since it always makes sure that the actions are performed in UI thread, my objective got fulfilled. The following did the trick.
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => { postProcess(); });
I'm new to Windows Phone 7 development, and am having a bit of trouble finding out how to download some data in the 'background', if you will. I know it is possible, because apps like ESPN, etc, display a "Loading ... .. ." while downloading their data, and the UI is still completely responsive. What I'm trying to do is download some Twitter data.
Here is what I have now, but it is blocking atm:
// Constructor:
// load the twitter data
WebClient twitter = new WebClient();
twitter.DownloadStringCompleted += new DownloadStringCompletedEventHandler(twitter_DownloadStringCompleted);
twitter.DownloadStringAsync(new Uri("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=badreligion"));
// Callback function:
void twitter_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
{
return;
}
XElement xmlTweets = XElement.Parse(e.Result);
TwitterListBox.ItemsSource = from tweet in xmlTweets.Descendants("status")
select new TwitterItem
{
ImageSource = tweet.Element("user").Element("profile_image_url").Value,
Message = tweet.Element("text").Value,
UserName = tweet.Element("user").Element("screen_name").Value
};
}
EDIT: Attempt at multithreading:
// in constructor
Dispatcher.BeginInvoke(new ThreadStart(StartTwitterUpdate));
// other functions
private void StartTwitterUpdate()
{
// load the twitter data
WebClient twitter = new WebClient();
twitter.DownloadStringCompleted += new DownloadStringCompletedEventHandler(twitter_DownloadStringCompleted);
twitter.DownloadStringAsync(new Uri("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=badreligion"));
}
void twitter_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
{
return;
}
XElement xmlTweets = XElement.Parse(e.Result);
TwitterListBox.ItemsSource = from tweet in xmlTweets.Descendants("status")
select new TwitterItem
{
ImageSource = tweet.Element("user").Element("profile_image_url").Value,
Message = tweet.Element("text").Value,
UserName = tweet.Element("user").Element("screen_name").Value
};
}
EDIT 2: Using HttpWebRequest, as suggested by Rico Suter, and with the help of this blog post, I think I've done it:
// constructor
StartTwitterUpdate();
private void StartTwitterUpdate()
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=badreligion"));
request.BeginGetResponse(new AsyncCallback(twitter_DownloadStringCompleted), request);
}
void twitter_DownloadStringCompleted(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
using (StreamReader streamReader1 =
new StreamReader(response.GetResponseStream()))
{
string resultString = streamReader1.ReadToEnd();
XElement xmlTweets = XElement.Parse(resultString);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
TwitterListBox.ItemsSource = from tweet in xmlTweets.Descendants("status")
select new TwitterItem
{
ImageSource = tweet.Element("user").Element("profile_image_url").Value,
Message = tweet.Element("text").Value,
UserName = "#" + tweet.Element("user").Element("screen_name").Value
};
});
}
}
I think the WebClient methods are partially blocking. The first part including DNS lookup is blocking, but the download itself is not.
See C# async methods still hang UI
Personally I'd call this a bug in the .net API (or even worse: broken by design)
As a workaround you can start the download in a separate thread. I recommend using the tasks API for that.
Task.Factory.StartNew(
()=>
{
twitter.DownloadStringAsync(new Uri("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=badreligion"));
}
);
Not optimal, since it occupies a thread while performing the DNS lookup, but should be acceptable in practice.
I think another problem with your code is that the callback will not happen on the main thread, but on a threadpool thread. You need to use a SynchronizationContext that posts the event to the main thread.
You have two options: HttpWebRequest and WebClient. Both classes are downloading in the background. Only difference: With WebClient the method twitter_DownloadStringCompleted will be called in UI thread so the parsing of the data will block the UI.
If you use HttpWebRequest the method will be called in another thread but to set the data for data binding or directly to a control you have to use something like this:
void twitter_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
// do your parsing with e.Result...
Deployment.Current.Dispatcher.BeginInvoke(() => {
// set your data (2) to UI
});
}
The code in (2) will be called in UI thread. Set your progressbar to visible in StartTwitterUpdate and set your progress bar to invisible in (2).
Check out this classes to simplify http calls (POST, FILES, GZIP, etc):
http://mytoolkit.codeplex.com/wikipage?title=Http