I'm having issues implementing a timer in C# code. high level overview of what I'm doing, i have a WPF app that updates data displayed to the user every three seconds based on a data feed. Most of the data on the window changes every three seconds but I'm trying to do an SQL query for a count and i only want the data to update every 5 minutes. So I built the SQL query shown below and the timer also shown below but the timer expects it to be a void when it had to return a string and i'm not sure how to get past this.
My original attempt at using a Timer (this runs in my main method):
Timer t = new Timer(TimeSpan.FromMinutes(5).TotalMilliseconds); // set the time (5 min in this case)
t.AutoReset = true;
t.Elapsed += new System.Timers.ElapsedEventHandler(SQLDataTotalCalls());
t.Start();
My Method for the SQL Query:
public string SQLDataTotalCalls(object sender, ElapsedEventArgs a)
{
DateTime dte = DateTime.Today;
string fixedStartDate = String.Format("{0:yyyy-MM-dd " + "05:00:00.000" + "}", dte);
string fixedEndDate = String.Format("{0:yyyy-MM-dd " + "05:00:00.000" + "}", dte.AddDays(1));
SqlConnection connection = null;
try
{
var dataSet = new DataSet();
connection = new SqlConnection("Data Source=QL1OADB1;Initial Catalog=OADB;User Id=username;Password=password;
connection.Open();
var command = new SqlCommand("SELECT COUNT(SOURCEID) AS 'MYCOUNT' "
+ "FROM [OADB].[oadb].[CmsCallHistory] "
+ "WHERE disposition = 2 and DISPSPLIT in (" + SkillNumber + ") AND SEGSTOP BETWEEN '" + fixedStartDate + "' and '" + fixedEndDate + "'",
connection)
{
CommandType = CommandType.Text
};
var dataAdapter = new SqlDataAdapter { SelectCommand = command };
dataAdapter.Fill(dataSet);
return dataSet.Tables[0].Rows[0]["MYCOUNT"].ToString();
}
catch (Exception e)
{
throw new Exception(e.Message, e);
}
finally
{
if (connection != null)
{
connection.Close();
}
}
}
Update:
Per the suggested answer below, I've changed the above to this:
async Task RunPeriodicQueryTotalCalls()
{
TimeSpan interval = TimeSpan.FromMinutes(5);
while (true)
{
await Task.Delay(interval);
string result = await Task.Run(SQLDataTotalCalls);
TotalDailyCalls = result;
}
}
I've updated my method for the SQL Query to have this declaration:
public string SQLDataTotalCalls() { ... }
But I get a compiler error:
error CS0121: The call is ambiguous between the following methods or properties: 'Task.Run<TResult>(Func<TResult>)' and 'Task.Run(Func<Task>)'
Supposing C# would let you subscribe your string-returning method to the void-declared event. What code, exactly, do you think would actually be receiving that return value? Where would you use it?
There isn't much context in your question, which makes it difficult, if not impossible, to understand exactly what you want the code to do. There's nothing in your question that indicates how you'd get the returned string value, nor what you'd do with it.
All that said, I think it likely your needs are better served using the Task Parallel Library API instead of a timer. This will allow you to easily move return values from background task to UI thread, as well as handle the timing for you.
For example:
async Task RunPeriodicQuery()
{
TimeSpan interval = TimeSpan.FromMinutes(5);
while (true)
{
await Task.Delay(interval);
string result = await Task.Run((Func<string>)SQLDataTotalCalls);
// do something with "result" here...you'll be in the UI thread, so make it quick
}
}
In the above, you would remove the parameters from your SQLDataTotalCalls() method declaration.
You will need to call the method RunPeriodicQuery() once, in some part of your code where you are initializing and would have otherwise created and started the timer. The implementation above uses an inifite while (true) loop, but of course you can introduce any mechanism you want to allow the method to exit if and when you want it to stop operating, such as using a bool field instead of true to control the loop operation.
If that does not address your question satisfactorily, please improve the question by editing it so that it includes a good Minimal, Complete, and Verifiable code example that shows clearly what you've tried, explain precisely what the code does, and what you want it to do instead.
Your code is calling the method, rather than pointing the event to your method.
// notice there are no parens, that's because you are pointing it at a method, not running the method
t.Elapsed += new System.Timers.ElapsedEventHandler(SQLDataTotalCalls);
A simpler way of doing this is:
t.Elapsed += SQLDataTotalCalls;
The timer should only be used to fire an event, not return a value. What is done in the event delegate (SQLDataTotalCalls) should pass the string to some other object or service to save the string.
You will need to change the signature of your SQLDataTotalCalls method to return void, so that it will match the signature required for the timer handler
Related
I'm trying to save some data from a GET request. I use StartCoroutine to request and I use Lambda expression for save the data.
My Code is this:
Using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour {
// Use this for initialization
public void Start () {
string url1 = "http://localhost/virtualTV/query/?risorsa=";
string ciao = "http://desktop-pqb3a65:8080/marmotta/resource/ef299b79-35f2-4942-a33b-7e4d7b7cbfb5";
url1 = url1 + ciao;
WWW www1 = new WWW(url1);
var main=new JSONObject(JSONObject.Type.OBJECT);
var final= new JSONObject(JSONObject.Type.OBJECT);;
StartCoroutine(firstParsing((value)=>{main = value;
final= main.Copy();
Debug.Log(main);
}));
Debug.Log(final);
}
public IEnumerator firstParsing( System.Action<JSONObject> callback)
{
string url2 = "http://localhost/virtualTV/FirstQuery/?risorsa=";
string ciao = "http://desktop-pqb3a65:8080/marmotta/resource/ef299b79-35f2-4942-a33b-7e4d7b7cbfb5";
url2 = url2 + ciao;
WWW www2 = new WWW(url2);
yield return www2;
string json = www2.text;
//Parsing del json con creazione di un array
var firstjson = new JSONObject(json);
var tempVideo = new JSONObject(JSONObject.Type.OBJECT);
var array2 = new JSONObject(JSONObject.Type.OBJECT);
tempVideo.AddField ("id", firstjson.GetField ("id"));
tempVideo.AddField ("type", firstjson.GetField ("type"));
tempVideo.AddField ("url", firstjson.GetField ("url"));
array2.Add (tempVideo);
yield return array2;
callback (array2);
Debug.Log ("First Run" + array2);
}
When I try to use FINAL after the command,
final=main.copy()
it is empty. Can you help me to save the value in the variable final? Thanks all.
A coroutine's execution is spread across many frames. When a coroutine encounters a yield return statement, it returns to the calling method, which finishes executing, till the task finishes.
In your case, the Debug.Log(final) statement in Start executes as soon as yield return www2; in firstParsing is executed. The callback hasn't been called yet which is why final is empty.
To be able to access the value in final after it has been assigned outside the callback function, you will have to set a bool which is set to true after final is assigned in the callback. Something like this:
StartCoroutine(firstParsing((value)=>{main = value;
final= main.Copy();
Debug.Log(main);
isFinalAssigned = true;
}));
// In another method
if(isFinalAssigned)
{
// Access final
}
You will have to note that the above if statement is useful only in a method that is called periodically like Update. If you're accessing final in a method that is called only once (like OnEnable) you will have to wait for final to be assigned. You can use another coroutine for this task like
IEnumerator DoSomethingWithFinal()
{
while(!isFinalAssigned)
yield return null; // Wait for next frame
// Do something with final
}
The easiest way out is to consume (access) final in your callback.
EDIT2: From your comments, you can do something like the following. You will have to use coroutines, because blocking the main game thread is not a good idea.
private JSONObject final = null; // Make final a field
Wherever you use final, you have two options.
Use a null check if(final == null) return; This can be impractical.
Wait for final to be assigned in a coroutine and do something as a callback. This is the only way you can do what you want cleanly.
Look below for the implementation.
// Calls callback after final has been assigned
IEnumerator WaitForFinal(System.Action callback)
{
while(final == null)
yield return null; // Wait for next frame
callback();
}
// This whole method depends on final.
// This should be similar to your method set up if you have
// good coding standards (not very long methods, each method does only 1 thing)
void MethodThatUsesFinal()
{
if (final == null)
{
// Waits till final is assigned and calls this method again
StartCoroutine(WaitForFinal(MethodThatUsesFinal));
return;
}
// use final
}
Suppose I have two threads, thread A and thread B. What happens in C# when thread A sets an object, call it object X, to a value, and thread B comes along and tries to fetch that object while it is getting set by thread A? Would C# throw an exception, would B receive object X before A makes changes to it, or would B receive object X after A makes changes to it?
I thought it was clear, but here is the code (and even with added synchronization I am still not 100% sure that this would cause the exact case I mentioned above to occur):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace WindowsFormsApplication8 {
public partial class Form1 : Form {
private AutoResetEvent waitEvent = new AutoResetEvent(false);
int objectX = 0;
public Form1() {
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
DateTime startTime = DateTime.Now;
Console.WriteLine("---------------------------- START # " + startTime + " ----------------------------------------------------------------------");
Thread A = new Thread(ThreadAWorkMethod);
Thread B = new Thread(ThreadBWorkMethod);
objectX = 0;
waitEvent = new AutoResetEvent(false);
A.Start();
B.Start();
A.Join();
B.Join();
Console.WriteLine("---------------------------- FINISHED AFTER " + (DateTime.Now - startTime).TotalMilliseconds + " ----------------------------");
}
public void ThreadAWorkMethod() {
waitEvent.WaitOne();
objectX = 5;
Console.WriteLine("Value has been changed to: " + objectX + " in thread A at " + DateTime.Now);
return;
}
public void ThreadBWorkMethod() {
waitEvent.Set();
string xInThreadB = objectX.ToString();
Console.WriteLine("Value in thread B: " + xInThreadB + " at " + DateTime.Now);
return;
}
}
}
The output of thread B to the console looks to be either 0 or 5, but even still you cannot say for certain which thread got serviced first even by checking the DateTime because both threads will have identical timestamps...and one output always makes it to the console first, so whose to say which got serviced first and if the threads actually collided on a get-set. So, in the end it seems that locking is implemented on variables in C# from a lower framework level as some people have mentioned.
Would C# throw an exception
No, it will likely not. There is nothing in the runtime that will check for this condition automatically and raise an exception.
However, the "value" being set may or may not be in a valid state at that point, so as soon as the result is used, whatever uses the value could easily raise an exception.
would B receive object X before A makes changes to it, or would B receive object X after A makes changes to it?
Either is possible. It's also possible, depending on the type of "object", that B would receive the object in an invalid state.
If you know multiple threads will be accessing a value, you should always take care to synchronize the access to your property. There are many options for synchronization, from using the (relatively) simple lock statement to other more complex synchronization options (such as ReaderWriterLock/ReaderWriterLockSlim, Mutex, Semaphore, etc).
So, something like this?
public class Test
{
public Test()
{
DateTime now = DateTime.Now;
Debug.WriteLine("Test started.");
A.Start();
B.Start();
A.Join();
B.Join();
Debug.WriteLine("Total time, in milliseconds, that you just spent in order to save StackOverflow members several minutes of their time: " + (DateTime.Now - now).TotalMilliseconds + " :)");
}
int objectX = 0;
Thread A = new Thread(ThreadAWorkMethod);
Thread B = new Thread(ThreadBWorkMethod);
public void ThreadAWorkMethod()
{
objectX = 5;
Debug.WriteLine("Value has been changed to: " + objectX.ToString() + "at " DateTime.Now.Milliseconds);
return;
}
public void ThreadBWorkMethod()
{
string xInThreadB = objectX.ToString();
Debug.WriteLine("Value in thread b: " + xInThreadB + "at " DateTime.Now.Milliseconds);
return;
}
}
I dunno, try it :)
Potentially similar question to C# Threading: a race condition example.
PS: That's why there is the lock and mutex in .NET Framework.
This situation might seem strange but this is what i have to do:
Situation, i have a sharepoint portal and there was such an issue that there might be a problem while retrieving user profiles that there might be too slow when a lot of people online and perfrom that kind of action, so there was made a descision to make a console application to test it out.
The console application needs to simulate behavior for retrieving the user profiles with as if many different users are doing that.
And there must be a log written.
The first question is this kind of testing a good way to really know where exactly the problme is?
And the other question is about my application, i have a strange behavior:
public class Program
{
static void Main(string[] args)
{
string filePath = #"C:\Users\User\Desktop\logfile.txt";
string siteUrl = #"http://siteurl";
int threads = 1;
//Multiplicator multiplicator = new Multiplicator(filePath, siteUrl, threads);
//Console.ReadLine();
for (int i = 0; i < 100; i++)
{
Thread t = new Thread(Execute);
t.Start();
}
Console.WriteLine("Main thread: " + Thread.CurrentThread.Name);
// Simultaneously, do something on the main thread.
}
static void Execute()
{
for (int i = 0; i < 100; i++)
{
using (SPSite ospSite = new SPSite(#"http://siteurl"))
{
SPServiceContext serviceContext = SPServiceContext.GetContext(ospSite);
UserProfileManager profileManager = new UserProfileManager(serviceContext);
UserProfile userProfile = profileManager.GetUserProfile("User Name");
string message = "Retrieved: " + userProfile.DisplayName + " on " +DateTime.Now + "by " Thread.CurrentThread.Name;
Console.WriteLine(message);
}
}
}
}
So the problem is i never get the name of the thread written why?
Thread.CurrentThread.Name is empty, is it normal, maybe i initialize the threading wrong? Altho many sources said that it is done like this?
You have not set the name of the thread. You should do so before you start it, and you can incorporate the iteration number in the name, if you like:
Thread t = new Thread(Execute);
t.Name = "My Thread" + i.ToString();
t.Start();
They are not given names automatically. The name can only be set once, after which you would get an InvalidOperationException
MSDN Reference: Thread.Name
Incidentally, creating 100 threads is probably not a good idea under normal circumstances.
You need to give it a name. Just when you create the thread, just name it based on i
Thread t = new Thread(Execute) { Name = i.ToString() };
Ok, I will go for your first question. No, there is a better way to do this.
Put the site under load. Maybe a friend can hit F5 all the time or you run a batch file with 1000 lines of a curl get
Attach the Visual Studio debugger to the webserver process
Hit break 10 times and see where it stops most of the time. That is your hotspot/problem.
This is called the poor mans profiler. It is built into every Visual Studio ;-)
In general, it is easy to find such problems by doing profiling. There are even sophisticated tools for this.
I have 3 comboboxes that are loaded with data from LINQ queries on page load. The problem is that queries contain so much data that it causes Internet Explorer to stop responding for a bit more than a minute.
As there are 3 queries my idea is to put them in 3 different threads, but the problem is at the end the only thing I get is the error saying: "Both DataSource and DataSourceID are defined on 'cbOrganizator'. Remove one definition."
cbOrganizator is a combobox.
Here is the code:
protected void Page_Load(object sender, EventArgs e)
{
Bind();
}
public void Osobe()
{
PravosudnaAkademijaEntities db = new PravosudnaAkademijaEntities();
var osoba = from o in db.osobas
orderby o.osoba_prezime
select new { o.osoba_id, imePrezime = o.osoba_prezime + " " + o.osoba_ime + " | " + o.tijelo.tijelo_naziv + " | " + o.radno_mjesto.rm_naziv_m };
cbPolaznik.DataSource = osoba;
cbPolaznik.DataTextField = "imePrezime";
cbPolaznik.DataValueField = "osoba_id";
cbPolaznik.DataBind();
cbPolaznik.Items.Insert(0, " ");
cbPredavac.DataSource = osoba;
cbPredavac.DataTextField = "imePrezime";
cbPredavac.DataValueField = "osoba_id";
cbPredavac.DataBind();
cbPredavac.Items.Insert(0, " ");
cbAOM.DataSource = osoba;
cbAOM.DataTextField = "imePrezime";
cbAOM.DataValueField = "osoba_id";
cbAOM.DataBind();
cbAOM.Items.Insert(0, " ");
}
public void Tijela()
{
PravosudnaAkademijaEntities db = new PravosudnaAkademijaEntities();
var tijelo = from t in db.tijeloes
orderby t.tijelo_naziv
select new { t.tijelo_id, sve = t.tijelo_naziv + " | " + t.mjesto.zupanija_drzava.zupanija_naziv };
cbOrganizator.DataSource = tijelo;
cbOrganizator.DataTextField = "sve";
cbOrganizator.DataValueField = "tijelo_id";
cbOrganizator.DataBind();
cbOrganizator.Items.Insert(0, " ");
}
public void Bind()
{
Thread tOsobe = new Thread(Osobe);
tOsobe.Start();
Thread tTijela = new Thread(Tijela);
tTijela.Start();
}
I don't know what's wrong so any help would be appreciated. The primary idea is to separate queries into threads so if my approach is wrong please let me know.
You're starting threads but not giving them a chance to finish before the page is loaded. I don't know how that results in your particular error, but if your page loads before the thread is completed, then you definitely won't get results.
I really don't see how you'll be able to accomplish what you're trying to do without AJAX.
if you do really want to do it with threads i would suggest performing the query on the threadpool. you can abstract your method so that it is called in delegate of the threadpool method
so i would replace bind with
ThreadPool.QueueUserWorkItem(new WaitCallback(Osobe));
ThreadPool.QueueUserWorkItem(new WaitCallback(Tijela));
change signature of Osobe, and Tijela to acccept a Object
eg.public void Osobe(object a )
you will also need to marshal the call across the thread as i am not sure if webforms will accept if the binding is happening on another thread.
All said and done is still feel the ajax method is the best way forward.
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.