C# 线程与ArcGIS
我刚刚偶然发现了Backgroundworker对象,它似乎是我正在寻找的工具,用于在执行计算时使GUI响应。我正在为ArcGIS编写IO插件C# 线程与ArcGIS,c#,multithreading,backgroundworker,arcgis,C#,Multithreading,Backgroundworker,Arcgis,我刚刚偶然发现了Backgroundworker对象,它似乎是我正在寻找的工具,用于在执行计算时使GUI响应。我正在为ArcGIS编写IO插件 public class Program { private volatile bool AbortOperation; Func<bool> AbortOperationDelegate; FinishProcessDelegate finishDelegate; UpdateGUIDelegate upda
public class Program
{
private volatile bool AbortOperation;
Func<bool> AbortOperationDelegate;
FinishProcessDelegate finishDelegate;
UpdateGUIDelegate updateGUIDelegate;
private delegate void UpdateGUIDelegate(int progress, object message);
private delegate void FinishProcessDelegate();
private void cmdBegin_Click(...)
{
// Create finish delegate, for determining when the thread is done.
finishDelegate = new FinishProcessDelegate(ProcessFinished);
// A delegate for updating the GUI.
updateGUIDelegate = new UpdateGUIDelegate(UpdateGUI);
// Create a delegate function for abortion.
AbortOperationDelegate = () => AbortOperation;
Thread BackgroundThread = new Thread(new ThreadStart(StartProcess));
// Force single apartment state. Required by ArcGIS.
BackgroundThread.SetApartmentState(ApartmentState.STA);
BackgroundThread.Start();
}
private void StartProcess()
{
// Update GUI.
updateGUIDelegate(0, "Beginning process...");
// Create object.
Converter converter = new Converter(AbortOperationDelegate);
// Parse the GUI update method to the converter, so it can update the GUI from within the converter.
converter.Progress += new ProcessEventHandler(UpdateGUI);
// Begin converting.
converter.Execute();
// Tell the main thread, that the process has finished.
FinishProcessDelegate finishDelegate = new FinishProcessDelegate(ProcessFinished);
Invoke(finishDelegate);
// Update GUI.
updateGUIDelegate(100, "Process has finished.");
}
private void cmdAbort_Click(...)
{
AbortOperation = true;
}
private void ProcessFinished()
{
// Post processing.
}
private void UpdateGUI(int progress, object message)
{
// If the call has been placed at the local thread, call it on the main thread.
if (this.pgStatus.InvokeRequired)
{
UpdateGUIDelegate guidelegate = new UpdateGUIDelegate(UpdateGUI);
this.Invoke(guidelegate, new object[] { progress, message });
}
else
{
// The call was made on the main thread, update the GUI.
pgStatus.Value = progress;
lblStatus.Text = (string)message;
}
}
}
public class Converter
{
private Func<bool> AbortOperation { get; set;}
public Converter(Func<bool> abortOperation)
{
AbortOperation = abortOperation;
}
public void Execute()
{
// Calculations using ArcGIS are done here.
while(...) // Insert your own criteria here.
{
// Update GUI, and replace the '...' with the progress.
OnProgressChange(new ProgressEventArgs(..., "Still working..."));
// Check for abortion at anytime here...
if(AbortOperation)
{
return;
}
}
}
public event ProgressEventHandler Progress;
private virtual void OnProgressChange(ProgressEventArgs e)
{
var p = Progress;
if (p != null)
{
// Invoke the delegate.
p(e.Progress, e.Message);
}
}
}
public class ProgressEventArgs : EventArgs
{
public int Progress { get; set; }
public string Message { get; set; }
public ProgressEventArgs(int _progress, string _message)
{
Progress = _progress;
Message = _message;
}
}
public delegate void ProgressEventHandler(int percentProgress, object userState);
我在ArcGIS之外做一些数据处理,使用backgroundworker可以很好地工作。但当我将数据插入ArcGIS时,backgroundworker似乎将持续时间增加了9倍左右。将处理代码置于DoWork方法之外,可将性能提高9倍
我在网上读过好几个地方,但是我没有多线程编程的经验,像STA和MTA这样的术语对我来说毫无意义。
我还尝试使用一个简单的线程实现,但结果类似
有人知道我能做些什么来使用ArcGIS处理和维护响应性GUI吗
编辑:我已经包括了一个与后台工作人员互动的示例。如果我将StartImporting方法中的代码放在cmdStart\u Click方法中,它的执行速度会快得多
private void StartImporting(object sender, DoWorkEventArgs e)
{
DateTime BeginTime = DateTime.Now;
// Create a new report object.
SKLoggingObject loggingObject = new SKLoggingObject("log.txt");
loggingObject.Start("Testing.");
SKImport skImporter = new SKImport(loggingObject);
try
{
// Read from a text box - no writing.
skImporter.Open(txtInputFile.Text);
}
catch
{
}
SKGeometryCollection convertedCollection = null;
// Create a converter object.
GEN_SK2ArcGIS converter = new GEN_SK2ArcGIS(loggingObject);
// Convert the data.
convertedCollection = converter.Convert(skImporter.GetGeometry());
// Create a new exporter.
ArcGISExport arcgisExporter = new ArcGISExport(loggingObject);
// Open the file.
// Read from a text box - no writing.
arcgisExporter.Open(txtOutputFile.Text);
// Insert the geometry collection.
try
{
arcgisExporter.Insert(convertedCollection);
}
catch
{
}
TimeSpan totalTime = DateTime.Now - BeginTime;
lblStatus.Text = "Done...";
}
private void ChangeProgress(object sender, ProgressChangedEventArgs e)
{
// If any message was passed, display it.
if (e.UserState != null && !((string)e.UserState).Equals(""))
{
lblStatus.Text = (string)e.UserState;
}
// Update the progress bar.
pgStatus.Value = e.ProgressPercentage;
}
private void ImportDone(object sender, RunWorkerCompletedEventArgs e)
{
// If the process was cancelled, note this.
if (e.Cancelled)
{
pgStatus.Value = 0;
lblStatus.Text = "Operation was aborted by user...";
}
else
{
}
}
private void cmdStart_Click(object sender, EventArgs e)
{
// Begin importing the sk file to the geometry collection.
// Initialise worker.
bgWorker = new BackgroundWorker();
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(ImportDone);
bgWorker.ProgressChanged += new ProgressChangedEventHandler(ChangeProgress);
bgWorker.DoWork += new DoWorkEventHandler(StartImporting);
bgWorker.WorkerReportsProgress = true;
bgWorker.WorkerSupportsCancellation = true;
// Start worker.
bgWorker.RunWorkerAsync();
}
private void cmdCancel_Click(object sender, EventArgs e)
{
bgWorker.CancelAsync();
}
您好,Casper为了维护响应良好的GUI,您通常需要在不同的线程中执行代码。通过使用BeginInvoke方法使用.net,这非常容易:
简而言之,将所有非GUI代码包含在一个单独的类(或多个类)中,而不是直接调用每个方法,而是创建一个委托并在该委托上调用BeginInvoke方法。然后,该方法将关闭并执行它的操作,而无需与GUI进一步交互。如果您希望它更新GUI(例如进度条),那么您可以从类中引发事件并从GUI中捕获它们,但是您需要确保以线程安全的方式更新控件。如果您希望在方法完成时更新GUI,那么您可以使用EndInvoke方法来处理该问题,通常是为了维护响应性GUI,您需要在不同的线程中执行执行工作的代码。通过使用BeginInvoke方法使用.net,这非常容易:
简而言之,将所有非GUI代码包含在一个单独的类(或多个类)中,而不是直接调用每个方法,而是创建一个委托并在该委托上调用BeginInvoke方法。然后,该方法将关闭并执行它的操作,而无需与GUI进一步交互。如果您希望它更新GUI(例如进度条),那么您可以从类中引发事件并从GUI中捕获它们,但是您需要确保以线程安全的方式更新控件。如果您希望在方法完成时更新GUI,那么可以使用EndInvoke方法来处理该问题,我一直在尝试找到解决方案,下面是我最后要做的。代码是从不同的文件中剪切和粘贴的,并呈现给大家,让大家了解我所做的工作。它演示了如何调用使用线程与ArcGIS通信的方法。代码允许我在主线程中更新GUI,中止操作,并执行操作后的操作。我最终使用了我最初发布的链接中的第一个线程部分 最初性能损失的原因可能是由于ArcGIS所需的单线程单元(STA)。Backgroundworker似乎是MTA,因此不适合使用ArcGIS 好了,我希望我没有忘记任何事情,并且可以自由编辑我的解决方案。这将有助于我和其他人为ArcGIS开发东西
public class Program
{
private volatile bool AbortOperation;
Func<bool> AbortOperationDelegate;
FinishProcessDelegate finishDelegate;
UpdateGUIDelegate updateGUIDelegate;
private delegate void UpdateGUIDelegate(int progress, object message);
private delegate void FinishProcessDelegate();
private void cmdBegin_Click(...)
{
// Create finish delegate, for determining when the thread is done.
finishDelegate = new FinishProcessDelegate(ProcessFinished);
// A delegate for updating the GUI.
updateGUIDelegate = new UpdateGUIDelegate(UpdateGUI);
// Create a delegate function for abortion.
AbortOperationDelegate = () => AbortOperation;
Thread BackgroundThread = new Thread(new ThreadStart(StartProcess));
// Force single apartment state. Required by ArcGIS.
BackgroundThread.SetApartmentState(ApartmentState.STA);
BackgroundThread.Start();
}
private void StartProcess()
{
// Update GUI.
updateGUIDelegate(0, "Beginning process...");
// Create object.
Converter converter = new Converter(AbortOperationDelegate);
// Parse the GUI update method to the converter, so it can update the GUI from within the converter.
converter.Progress += new ProcessEventHandler(UpdateGUI);
// Begin converting.
converter.Execute();
// Tell the main thread, that the process has finished.
FinishProcessDelegate finishDelegate = new FinishProcessDelegate(ProcessFinished);
Invoke(finishDelegate);
// Update GUI.
updateGUIDelegate(100, "Process has finished.");
}
private void cmdAbort_Click(...)
{
AbortOperation = true;
}
private void ProcessFinished()
{
// Post processing.
}
private void UpdateGUI(int progress, object message)
{
// If the call has been placed at the local thread, call it on the main thread.
if (this.pgStatus.InvokeRequired)
{
UpdateGUIDelegate guidelegate = new UpdateGUIDelegate(UpdateGUI);
this.Invoke(guidelegate, new object[] { progress, message });
}
else
{
// The call was made on the main thread, update the GUI.
pgStatus.Value = progress;
lblStatus.Text = (string)message;
}
}
}
public class Converter
{
private Func<bool> AbortOperation { get; set;}
public Converter(Func<bool> abortOperation)
{
AbortOperation = abortOperation;
}
public void Execute()
{
// Calculations using ArcGIS are done here.
while(...) // Insert your own criteria here.
{
// Update GUI, and replace the '...' with the progress.
OnProgressChange(new ProgressEventArgs(..., "Still working..."));
// Check for abortion at anytime here...
if(AbortOperation)
{
return;
}
}
}
public event ProgressEventHandler Progress;
private virtual void OnProgressChange(ProgressEventArgs e)
{
var p = Progress;
if (p != null)
{
// Invoke the delegate.
p(e.Progress, e.Message);
}
}
}
public class ProgressEventArgs : EventArgs
{
public int Progress { get; set; }
public string Message { get; set; }
public ProgressEventArgs(int _progress, string _message)
{
Progress = _progress;
Message = _message;
}
}
public delegate void ProgressEventHandler(int percentProgress, object userState);
公共类程序
{
私人易变业务;
函数中止操作委托;
FinishProcessDelegate finishDelegate;
UpdateGUIDelegate UpdateGUIDelegate;
私有委托void UpdateGUIDelegate(int进程,对象消息);
私有委托void FinishProcessDelegate();
私有void cmdBegin\u单击(…)
{
//创建完成委托,用于确定线程何时完成。
finishDelegate=新的FinishProcessDelegate(ProcessFinished);
//用于更新GUI的委托。
updateGUIDelegate=新的updateGUIDelegate(UpdateGUI);
//创建用于中止的委托函数。
AbortOperationDelegate=()=>AbortOperation;
线程背景线程=新线程(新线程开始(StartProcess));
//强制单间公寓状态。ArcGIS要求。
BackgroundThread.SetApartmentState(ApartmentState.STA);
BackgroundThread.Start();
}
私有void StartProcess()
{
//更新GUI。
updateGUIDelegate(0,“正在开始进程…”);
//创建对象。
转换器=新转换器(中止操作委托);
//将GUI更新方法解析到转换器,以便它可以从转换器内更新GUI。
converter.Progress+=新的ProcessEventHandler(UpdateGUI);
//开始转换。
converter.Execute();
//告诉主线程进程已经完成。
FinishProcessDelegate finishDelegate=新的FinishProcessDelegate(ProcessFinished);
调用(finishDelegate);
//更新GUI。
updateGUIDelegate(100,“进程已完成”);
}
私有void cmdAbort_单击(…)
{
中止操作=真;
}
私有void ProcessFinished()
{
//后处理。
}
私有void UpdateGUI(int进程,对象消息)
{
//如果调用已放在本地线程上,则在主线程上调用它。
if(this.pgStatus.invokererequired)
{
UpdateGUIDelegate guidelegate=新的UpdateGUIDelegate(UpdateGUI);
调用(guidelegate,新对象[]{progress,message});
}
其他的
{
//调用是在主线程上进行的,更新GUI。
pgStatus.Value=进度;
lblStatus.Text=(字符串)消息;
}
}
}
公众的
protected virtual void StatusUpdateNotify(ProgressState progressState)
{
if (BackgroundWorker.CancellationPending)
{
throw new OperationCanceledException();
}
BackgroundWorker.ReportProgress(progressState.Progress, progressState);
}