运行指定时间间隔的方法c#
Run a method at a specified interval c#
我有一个按钮,按下该按钮可获取远程 PC 上各种 windows 服务的状态。我想每分钟自动刷新此按钮,以便始终显示服务的最新状态。
我尝试设置一个计时器,但我一直收到错误 "Cross-thread operation not valid: Control 'btnRefreshServices' accessed from a thread other than the thread it was created on"
感谢任何帮助
private void btnRefreshServices_Click(
object sender,
EventArgs eventArgs)
{
this.btnRefreshServices.Enabled = false;
// Setting up progress bar in a separate thread to update the progress bar
// This is necessary so that the dialog doesn't freeze while the progress bar is reporting its progress
this.prgbServiceStatus.Minimum = 1;
this.prgbServiceStatus.Maximum = 11;
this.prgbServiceStatus.Step = 1;
this.prgbServiceStatus.Value = 1;
this.prgbServiceStatus.Increment(1);
this.prgbServiceStatus.PerformStep();
var _backgroundWorker = new BackgroundWorker();
_backgroundWorker.ProgressChanged += ProgressChanged;
_backgroundWorker.DoWork += DoWork;
_backgroundWorker.WorkerReportsProgress = true;
_backgroundWorker.RunWorkerAsync();
_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(RunWorkerCompleted);
}
private void DoWork(
object sender,
DoWorkEventArgs doWorkEventArgs)
{
// Get the current status of each Windows service and reflect the progress in the progress bar
// NOTE: If you add a new service, divide the number of services by 100 and update each call to report progress
((BackgroundWorker)sender).ReportProgress(15);
CurrentStatus(
this.grdResults,
ServerA,
ServiceName,
RowIndexA);
((BackgroundWorker)sender).ReportProgress(25);
CurrentStatus(
this.grdResults,
ServerB,
ServiceNameB,
RowIndexB);
((BackgroundWorker)sender).ReportProgress(35);
}
我正在为计时器使用类似这段代码的代码
Timer myTimer = new Timer();
myTimer.Elapsed += new ElapsedEventHandler(DisplayTimeEvent);
myTimer.Interval = 1000; // 1000 ms is one second
myTimer.Start();
public static void DisplayTimeEvent(object source, ElapsedEventArgs e)
{
// code here will run every second
}
使用 Emile Pels 代码我能够解决我的问题。
public frmServicesManager()
{
InitializeComponent();
// The interval in milliseconds (1000 ms = 1 second)
const double interval = 5000.0;
// Create a new timer
new System.Timers.Timer()
{
Enabled = true,
Interval = interval
}.Elapsed += TimerCallback;
}
private void TimerCallback(
object sender,
ElapsedEventArgs elapsedEventArgs)
{
// SignalTime is now of type DateTime and contains the value indicating when the timer's Elapsed event was raised
var _signalTime = elapsedEventArgs.SignalTime;
// Create a new Action
var _setButtonClick = new Action<DateTime>(dateTime => this.btnRefreshServices.PerformClick());
// Check if we can access the control from this thread
if (this.btnRefreshServices.InvokeRequired)
{
// We can't access the label from this thread,so we'll call invoke so it is executed from the thread the it was created on
this.btnRefreshServices.Invoke(_setButtonClick, _signalTime);
}
}
使用 System.Windows.Forms.Timer,或从另一个线程设置按钮的 属性,如下所示:
myButton.Invoke(new Action<string>((text) => myButton.Text = text), "New button text");
编辑:这是一个有更多解释的例子。
您收到该错误的原因是您正在尝试访问在其他线程上创建的控件,但该线程不起作用。您将不得不调用控件的 Invoke()
方法;执行您在创建控件的线程上传递给它的委托。
您可以使用的委托之一是 Action
,正如我稍后在 post.
中演示的那样
对于以下示例,我使用了 System.Timers.Timer
,并创建了一个新的 Winforms 项目并仅向其中添加了 Label
。它的名字是 timeLabel.
我将此代码放入表单的构造函数中:
//The interval in milliseconds (1000 ms = 1 second)
const double interval = 1000.0;
//Create a new timer
new System.Timers.Timer()
{
Enabled = true, //Start it right away
Interval = interval //Set the interval
}.Elapsed += TimerCallback; //Register a handler for the elapsed event
这将创建一个新的计时器并注册一个回调来处理其 Elapsed 事件,该事件定义如下:
private void TimerCallback(object sender, System.Timers.ElapsedEventArgs e)
{
const string baseString = "The event was raised at {0}";
//signalTime is now of type DateTime and contains the value
//indicating when the timer's Elapsed event was raised
var signalTime = e.SignalTime;
//Create a new Action - delegate - which takes a string argument
var setLabelText = new Action<DateTime>(dt =>
{
//If the amount of seconds in the dt argument is an even number,
//set the timeLabel's forecolor to red; else, make it green
timeLabel.ForeColor = dt.Second % 2 == 0 ? Color.Red : Color.Green;
//Format the baseString to display the time in dt
timeLabel.Text = string.Format(baseString, dt.ToLongTimeString());
});
//Check if we can access the control from this thread
if (timeLabel.InvokeRequired) {
//We can't access the label from this thread,
//so we'll call invoke so it is executed from
//the thread the it was created on
timeLabel.Invoke(setLabelText, signalTime);
}
else {
//The label's text can be set from this thread,
//we'll just call the delegate without Invoke()
setLabelText(signalTime);
}
}
此特定示例每秒将标签的文本更改为当前时间,如果秒数为偶数,则使标签的前景色为红色:如果为奇数,则颜色将设置为绿色。该程序可能看起来不是很有用,但它演示了如何从其他线程访问控件;一旦你掌握了这个例子,它应该可以帮助你扩展它以满足你的需要。
我不清楚 BackgroundWorker
代码与您的问题有什么关系。好像跟跨线程问题没有关系,跟你定期刷新按钮也没有关系。
就问题的内容而言,您应该能够使用正确的 Timer
class(.NET 中至少有三个),System.Windows.Forms.Timer
.在这种情况下,您的代码将如下所示:
System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
myTimer.Tick += DisplayTimeEvent;
myTimer.Interval = 1000; // 1000 ms is one second
myTimer.Start();
public static void DisplayTimeEvent(object source, EventArgs e)
{
// code here will run every second
}
上面的代码应该在 Winforms 模块中,因此 System.Windows.Forms
命名空间应该已经在范围内,但我已经完全限定上面的 Timer
class 名称只是为了清楚起见。
另请注意事件名称不同:Tick
而不是 Elapsed
。并且事件处理程序签名略有不同。
使用此 Timer
class 而不是您使用的 Tick
事件处理程序将在 UI 线程上调用,避免任何跨线程完全问题。
我有一个按钮,按下该按钮可获取远程 PC 上各种 windows 服务的状态。我想每分钟自动刷新此按钮,以便始终显示服务的最新状态。
我尝试设置一个计时器,但我一直收到错误 "Cross-thread operation not valid: Control 'btnRefreshServices' accessed from a thread other than the thread it was created on"
感谢任何帮助
private void btnRefreshServices_Click(
object sender,
EventArgs eventArgs)
{
this.btnRefreshServices.Enabled = false;
// Setting up progress bar in a separate thread to update the progress bar
// This is necessary so that the dialog doesn't freeze while the progress bar is reporting its progress
this.prgbServiceStatus.Minimum = 1;
this.prgbServiceStatus.Maximum = 11;
this.prgbServiceStatus.Step = 1;
this.prgbServiceStatus.Value = 1;
this.prgbServiceStatus.Increment(1);
this.prgbServiceStatus.PerformStep();
var _backgroundWorker = new BackgroundWorker();
_backgroundWorker.ProgressChanged += ProgressChanged;
_backgroundWorker.DoWork += DoWork;
_backgroundWorker.WorkerReportsProgress = true;
_backgroundWorker.RunWorkerAsync();
_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(RunWorkerCompleted);
}
private void DoWork(
object sender,
DoWorkEventArgs doWorkEventArgs)
{
// Get the current status of each Windows service and reflect the progress in the progress bar
// NOTE: If you add a new service, divide the number of services by 100 and update each call to report progress
((BackgroundWorker)sender).ReportProgress(15);
CurrentStatus(
this.grdResults,
ServerA,
ServiceName,
RowIndexA);
((BackgroundWorker)sender).ReportProgress(25);
CurrentStatus(
this.grdResults,
ServerB,
ServiceNameB,
RowIndexB);
((BackgroundWorker)sender).ReportProgress(35);
}
我正在为计时器使用类似这段代码的代码
Timer myTimer = new Timer();
myTimer.Elapsed += new ElapsedEventHandler(DisplayTimeEvent);
myTimer.Interval = 1000; // 1000 ms is one second
myTimer.Start();
public static void DisplayTimeEvent(object source, ElapsedEventArgs e)
{
// code here will run every second
}
使用 Emile Pels 代码我能够解决我的问题。
public frmServicesManager()
{
InitializeComponent();
// The interval in milliseconds (1000 ms = 1 second)
const double interval = 5000.0;
// Create a new timer
new System.Timers.Timer()
{
Enabled = true,
Interval = interval
}.Elapsed += TimerCallback;
}
private void TimerCallback(
object sender,
ElapsedEventArgs elapsedEventArgs)
{
// SignalTime is now of type DateTime and contains the value indicating when the timer's Elapsed event was raised
var _signalTime = elapsedEventArgs.SignalTime;
// Create a new Action
var _setButtonClick = new Action<DateTime>(dateTime => this.btnRefreshServices.PerformClick());
// Check if we can access the control from this thread
if (this.btnRefreshServices.InvokeRequired)
{
// We can't access the label from this thread,so we'll call invoke so it is executed from the thread the it was created on
this.btnRefreshServices.Invoke(_setButtonClick, _signalTime);
}
}
使用 System.Windows.Forms.Timer,或从另一个线程设置按钮的 属性,如下所示:
myButton.Invoke(new Action<string>((text) => myButton.Text = text), "New button text");
编辑:这是一个有更多解释的例子。
您收到该错误的原因是您正在尝试访问在其他线程上创建的控件,但该线程不起作用。您将不得不调用控件的 Invoke()
方法;执行您在创建控件的线程上传递给它的委托。
您可以使用的委托之一是 Action
,正如我稍后在 post.
对于以下示例,我使用了 System.Timers.Timer
,并创建了一个新的 Winforms 项目并仅向其中添加了 Label
。它的名字是 timeLabel.
我将此代码放入表单的构造函数中:
//The interval in milliseconds (1000 ms = 1 second)
const double interval = 1000.0;
//Create a new timer
new System.Timers.Timer()
{
Enabled = true, //Start it right away
Interval = interval //Set the interval
}.Elapsed += TimerCallback; //Register a handler for the elapsed event
这将创建一个新的计时器并注册一个回调来处理其 Elapsed 事件,该事件定义如下:
private void TimerCallback(object sender, System.Timers.ElapsedEventArgs e)
{
const string baseString = "The event was raised at {0}";
//signalTime is now of type DateTime and contains the value
//indicating when the timer's Elapsed event was raised
var signalTime = e.SignalTime;
//Create a new Action - delegate - which takes a string argument
var setLabelText = new Action<DateTime>(dt =>
{
//If the amount of seconds in the dt argument is an even number,
//set the timeLabel's forecolor to red; else, make it green
timeLabel.ForeColor = dt.Second % 2 == 0 ? Color.Red : Color.Green;
//Format the baseString to display the time in dt
timeLabel.Text = string.Format(baseString, dt.ToLongTimeString());
});
//Check if we can access the control from this thread
if (timeLabel.InvokeRequired) {
//We can't access the label from this thread,
//so we'll call invoke so it is executed from
//the thread the it was created on
timeLabel.Invoke(setLabelText, signalTime);
}
else {
//The label's text can be set from this thread,
//we'll just call the delegate without Invoke()
setLabelText(signalTime);
}
}
此特定示例每秒将标签的文本更改为当前时间,如果秒数为偶数,则使标签的前景色为红色:如果为奇数,则颜色将设置为绿色。该程序可能看起来不是很有用,但它演示了如何从其他线程访问控件;一旦你掌握了这个例子,它应该可以帮助你扩展它以满足你的需要。
我不清楚 BackgroundWorker
代码与您的问题有什么关系。好像跟跨线程问题没有关系,跟你定期刷新按钮也没有关系。
就问题的内容而言,您应该能够使用正确的 Timer
class(.NET 中至少有三个),System.Windows.Forms.Timer
.在这种情况下,您的代码将如下所示:
System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
myTimer.Tick += DisplayTimeEvent;
myTimer.Interval = 1000; // 1000 ms is one second
myTimer.Start();
public static void DisplayTimeEvent(object source, EventArgs e)
{
// code here will run every second
}
上面的代码应该在 Winforms 模块中,因此 System.Windows.Forms
命名空间应该已经在范围内,但我已经完全限定上面的 Timer
class 名称只是为了清楚起见。
另请注意事件名称不同:Tick
而不是 Elapsed
。并且事件处理程序签名略有不同。
使用此 Timer
class 而不是您使用的 Tick
事件处理程序将在 UI 线程上调用,避免任何跨线程完全问题。