Okay so basically I have a function that returns a string, but to get that string it uses webrequest which means while it's doing that webrequest the form is locking up unless I put it in a different thread.
But I can't figure out a way to capture the returned data in a thread since it's started using thread.start and that's a void.
Any help please?
Current code if it matters to anyone:
string CreateReqThread(string UrlReq)
{
System.Threading.Thread NewThread = new System.Threading.Thread(() => CreateReq(UrlReq));
string ReturnedData = "";
return ReturnedData;
}
string CreateReq(string url)
{
try
{
WebRequest SendReq = WebRequest.Create(url);
SendReq.Credentials = CredentialCache.DefaultCredentials;
SendReq.Proxy = WebRequest.DefaultWebProxy; //For closed port networks like colleges
SendReq.Proxy.Credentials = CredentialCache.DefaultCredentials;
SendReq.Timeout = 15000;
System.IO.StreamReader Reader = new System.IO.StreamReader(SendReq.GetResponse().GetResponseStream());
string Response = Reader.ReadToEnd();
Reader.Close();
return Response;
}
catch (WebException e)
{
EBox(e.Message, "Unknown Error While Connecting");
return null;
}
}
A common means of doing this is to use a Task<T> instead of a thread:
Task<string> CreateReqThread(string UrlReq)
{
return Task.Factory.StartNew() => CreateReq(UrlReq));
// In .NET 4.5, you can use (or better yet, reimplement using await/async directly)
// return Task.Run(() => CreateReq(UrlReq));
}
You can then call Task<T>.Result to get the returned value (later), when it's needed, or schedule a continuation on the task which will run when it completes.
This could look something like:
var request = CreateReqThread(theUri);
request.ContinueWith(t =>
{
// Shove results in a text box
this.textBox.Text = t.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
This also works perfectly with the new await/async support in C# 5.
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've got a list of Accounts. I want to login with all accounts on a website. I want to use Parallel.ForEach to process all accounts.
This is what my code looks like:
Parallel.ForEach(Accounts,
acc =>
{
acc.WebProxy = null;
acc.Channel = "pelvicpaladin__";
Debug.WriteLine("Connecting to {0}.", new object[] { acc.Username });
acc.ConnectToIrc().Wait();
});
Everything works fine except one single problem:
The first Account in the Accounts-list does not work. Internal I have to use more than one request (it is a bit more than just logging in). The first request just does nothing. If I break the debugger, there is no available source.
I've got about 12 accounts. I've tried to remove the first account from the list. But the problem is still the same (now the new first (old second) account fails).
And now the very strange point:
If I don't use Parallel.For, everything works fine.
foreach (var acc in Accounts)
{
acc.WebProxy = null;
Debug.WriteLine("Connecting to {0}.", new object[] { acc.Username });
await acc.ConnectToIrc();
}
Again: Everything works except the first account from the list. It is always the first (it does not depend on how much accounts the list contains or which account is the first account).
Does anyone has any idea?
EDIT: This is how I create WebRequests:
private async Task<string> GetResponseContent(HttpWebRequest request)
{
if (request == null)
throw new ArgumentNullException("request");
using (var response = await request.GetResponseAsync())
{
return await GetResponseContent((HttpWebResponse)response);
}
}
private async Task<string> GetResponseContent(HttpWebResponse response)
{
if (response == null)
throw new ArgumentNullException("response");
using (var responseStream = response.GetResponseStream())
{
return await new StreamReader(responseStream).ReadToEndAsync();
}
}
private HttpWebRequest GetRequest(string url)
{
if (String.IsNullOrWhiteSpace(url))
throw new ArgumentNullException("url");
try
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.CookieContainer = _cookieContainer;
request.Referer = url;
request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8";
request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.4) Gecko/20091016 Firefox/3.5.4 GTB6 (.NET CLR 3.5.30729)";
if (_webProxy != null)
request.Proxy = WebProxy.WebProxy;
request.KeepAlive = true;
request.Timeout = 30000;
return request;
}
catch (Exception ex)
{
ErrorLogger.Log(String.Format("Could not create Request on {0}.", url), ex);
return null;
}
}
You're running into the typical await deadlock situation. The problem is that your calls to ConnectToIrc are using await and capturing the synchronization context. They're trying to marshall the continuations to the main thread. The problem is that your main thread is busy blocking on the call to Parallel.ForEach. It's not allowing any of those continuations to run. The main thread is waiting on the continuations to continue, the continuations are waiting on the main thread to be free to run. Deadlock.
This is (one reason) why you shouldn't be synchronously waiting on asynchronous operations.
Instead just start up all of the asynchronous operations and use WhenAll to wait for them all to finish. There's no need to create new threads, or use the thread pool, etc.
var tasks = new List<Task>();
foreach (var acc in Accounts)
{
acc.WebProxy = null;
Debug.WriteLine("Connecting to {0}.", new object[] { acc.Username });
tasks.Add(acc.ConnectToIrc());
}
await Task.WhenAll(tasks);
This, unlike your second example, will perform all of the async operations in parallel, while still waiting asynchronously.
Updated again:
var tasks = Accounts.Select(MyTask).ToList();
await Task.WhenAll(tasks);
then you can write a named method:
private Task MyTask(Account acc)
{
acc.WebProxy = null;
Debug.WriteLine("Connecting to {0}.", new object[] { acc.Username });
return acc.ConnectToIrc();
}
thanks for the tip
I am trying to send url requests and get responses back asynchronously, but when I use the .fromAsync method with the .ContinueWith method the task status never changes from WaitingForActivation to running. I have tried using task.Start() but it can not be used with ContinueWith. How would I go by changing the tasks status to running in my code?
public void pageCheck(){
IAsyncResult asyncResult;
Uri uri = new Uri(TempURL); //TempUrl is assigned a string beforehand
HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(uri);
try{
Task<WebResponse> task = Task.Factory.FromAsync(
myReq.BeginGetResponse,
asyncResult => myReq.EndGetResponse(asyncResult),
(Object)null);
task.ContinueWith(t =>
{
var responseCode = (HttpWebResponse)t.Result;
ReadStreamFromResponse(t.Result);
if(responseCode.StatusCode == HttpStatusCode.OK){
//updatestatus
reponseCode.Close();
}
}
);
}
//catch exceptions
}
private String ReadStreamFromResponse(WebResponse response) {
StreamReader responseStream = new StreamReader(response.GetResponseStream());
string str = responseStream.ReadToEnd();
return str;
}
Task.Factory.FromAsync automatically schedules the task. From the documentation:
The beginMethod delegate is started on the thread that FromAsync is running on. This method throws any exceptions thrown by the beginMethod.
You would typically write this without lambdas:
Task<WebResponse> task = Task<WebResponse>.Factory.FromAsync(
myReq.BeginGetResponse,
myReq.EndGetResponse,
null);
If you're using .NET 4.5 and C# 5, you cna use WebRequest.GetResponseAsync directly, as well.
Okay, before I go on, let me state that my background is in web scripting; so applications are very foreign to me. I know very little about .NET and I've been skating by on my limited knowledge.
Anyways, in my application, I have an OAuth httpRequest. The request itself works fine, it gets the data I need from the web API. However, the problem is that whenever I click the button that activates the request, my program freezes for a few seconds until the request is finished. I also have another request which is done automatically every 60 seconds. Which of course means every 60 seconds, my program freezes for a few seconds. How to fix this?
private string twitchCallAPI(string accessKey, string accessSecret, string endpointURI, string httpMethod)
{
OAuthHttpWebRequest httpRequest = new OAuthHttpWebRequest();
httpRequest.ConsumerToken = new OAuthToken { Token = this.twitchConKey, TokenSecret = this.twitchConSecret };
httpRequest.Token = new OAuthToken() { Token = accessKey, TokenSecret = accessSecret };
httpRequest.SetUri(endpointURI);
httpRequest.Method = httpMethod;
try
{
using (var response = httpRequest.GetResponse())
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
return reader.ReadToEnd();
}
}
}
catch (WebException ex)
{
using (var reader = new StreamReader(ex.Response.GetResponseStream()))
{
System.Windows.MessageBox.Show(reader.ReadToEnd());
}
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex.ToString());
}
return string.Empty;
}
You could use a background worker
Shortly said, do request in task and update UI thread with UI synchronization context
TaskFactory.StartNew(()=>
{
//do web request
})
.ContinueWith(() =>
{
this.TextBlock1.Text = "Complete";
}, TaskScheduler.FromCurrentSynchronizationContext());
You can try using Async methods, that is, using a different thread to wait for the response of the request. Its a solution that you can explore.
http://msdn.microsoft.com/en-us/library/86wf6409%28v=vs.100%29.aspx
You can use await keyword:
private async void OnButtonClick()
{
TextBox.Text = await twitchCallAPIAsync(accessKey, accessSecret, endpointURI, httpMethod);
}
The main reason of this is because your application is waiting the methods you launch to finish. You have to take a look at the 'async' concept.
A program executing an 'async' method continue its workflow, and doesn't wait the method to produce a result.