通过列表<折线>线程之间折线>
我有一个 List
,我需要在第二个线程中生成它,以免失去 GUI 响应能力。线程完成后,我需要访问 GUI 线程中的 List
进行显示。这就是我遇到问题的地方。我使用匿名方法调用 UI 线程上的复制逻辑,将折线复制到已在 UI 线程上初始化的 List
中。我收到“调用线程无法访问该对象,因为另一个线程拥有它。”
我尝试过 Clone 和 DeepCopy 扩展方法来从线程列表复制到 UI 列表,但 Polyline 不可序列化。
Class ThreadedExecuter<T> where T : class
{
public delegate void CallBackDelegate(T returnValue);
public delegate T MethodDelegate();
private CallBackDelegate callback;
private MethodDelegate method;
private Thread t;
public ThreadedExecuter(MethodDelegate method, CallBackDelegate callback)
{
this.method = method;
this.callback = callback;
t = new Thread(this.Process);
//XPSDocument requires STA
t.SetApartmentState(ApartmentState.STA);
}
public void Start()
{
t.Start();
}
private void Process()
{
T stuffReturned = method();
callback(stuffReturned);
}
}
void StartXPStoPolyThread()
{
ThreadedExecuter<List<Polyline>> executer = new ThreadedExecuter<List<Polyline>>(GeneratePolyFromXPS, PolyFromXPSComplete);
executer.Start();
}
List<Polyline> GeneratePolyFromXPS()
{
string file = @"C:\Users\Company\Desktop\shapes6.xps";
Company.XPStoPolyline.XPStoPolylineHelper myXPSPoly = new Company.XPStoPolyline.XPStoPolylineHelper(); //init brushes
List<Polyline> LocalList = myXPSPoly.ReadFileOutputPolylineList(file, CurrentConfig.ImportTolerance);
return LocalList;
}
void PolyFromXPSComplete(List<Polyline> PolylistIn)
{
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new Action(() =>
{
PolylineList.Clear(); //already initialized in UI thread
PolylineList = new List<Polyline>(PolylistIn.Capacity);
foreach (Polyline currentPolyline in PolylistIn)
{
Polyline correctedPolyLine = new Polyline();
PointCollection myPointCollection = new PointCollection();
Point pointToAdd = currentPolyline.Points[0]; //"The calling thread cannot access this object because a different thread owns it."
myPointCollection.Add(pointToAdd);
for (int i = 1; i < currentPolyline.Points.Count; i++)
{
....copy points
}
correctedPolyLine.Points = myPointCollection;
correctedPolyLine.Stroke = currentPolyline.Stroke;
correctedPolyLine.StrokeThickness = 1;
PolylineList.Add(correctedPolyLine);
}
//display for testing
VectorGridCanvas.Children.Clear();
foreach (Polyline myPolyLine in PolylineList)
{
VectorGridCanvas.Children.Add(myPolyLine);
}
}));
}
I have a List<Polyline>
that I need to generate in a second thread so as not to lose GUI responsiveness. After The thread is complete I need to access the List<Polyline>
in the GUI thread to display. This is where I am running into a problem. I am invoking the copy logic on the UI thread with an anonymous method, copying the polylines into a List<Polyline>
already initialized on the UI thread. I am getting "The calling thread cannot access this object because a different thread owns it."
I have tried Clone and DeepCopy extension methods to copy from the thread List to the UI list but Polyline is not serializable.
Class ThreadedExecuter<T> where T : class
{
public delegate void CallBackDelegate(T returnValue);
public delegate T MethodDelegate();
private CallBackDelegate callback;
private MethodDelegate method;
private Thread t;
public ThreadedExecuter(MethodDelegate method, CallBackDelegate callback)
{
this.method = method;
this.callback = callback;
t = new Thread(this.Process);
//XPSDocument requires STA
t.SetApartmentState(ApartmentState.STA);
}
public void Start()
{
t.Start();
}
private void Process()
{
T stuffReturned = method();
callback(stuffReturned);
}
}
void StartXPStoPolyThread()
{
ThreadedExecuter<List<Polyline>> executer = new ThreadedExecuter<List<Polyline>>(GeneratePolyFromXPS, PolyFromXPSComplete);
executer.Start();
}
List<Polyline> GeneratePolyFromXPS()
{
string file = @"C:\Users\Company\Desktop\shapes6.xps";
Company.XPStoPolyline.XPStoPolylineHelper myXPSPoly = new Company.XPStoPolyline.XPStoPolylineHelper(); //init brushes
List<Polyline> LocalList = myXPSPoly.ReadFileOutputPolylineList(file, CurrentConfig.ImportTolerance);
return LocalList;
}
void PolyFromXPSComplete(List<Polyline> PolylistIn)
{
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new Action(() =>
{
PolylineList.Clear(); //already initialized in UI thread
PolylineList = new List<Polyline>(PolylistIn.Capacity);
foreach (Polyline currentPolyline in PolylistIn)
{
Polyline correctedPolyLine = new Polyline();
PointCollection myPointCollection = new PointCollection();
Point pointToAdd = currentPolyline.Points[0]; //"The calling thread cannot access this object because a different thread owns it."
myPointCollection.Add(pointToAdd);
for (int i = 1; i < currentPolyline.Points.Count; i++)
{
....copy points
}
correctedPolyLine.Points = myPointCollection;
correctedPolyLine.Stroke = currentPolyline.Stroke;
correctedPolyLine.StrokeThickness = 1;
PolylineList.Add(correctedPolyLine);
}
//display for testing
VectorGridCanvas.Children.Clear();
foreach (Polyline myPolyLine in PolylineList)
{
VectorGridCanvas.Children.Add(myPolyLine);
}
}));
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
由于 Polyline 是一个 WPF UIElement 类,因此它绑定到 UI 线程。如果您尝试从另一个线程访问其任何属性,它将引发异常。
请注意,Polyline 是一个显示类,而不是数据类型。您不应该在后台线程中摆弄 WPF 控件或其他显示元素。 WPF 将在每一步中与您对抗。
最好的选择是使用您自己设计的中间数据类型执行 XPS 到折线的转换计算。除了 UIElement 之外的任何东西都可以。值类型结构可能是合适的。一旦您完成了后台线程所需的密集计算体操,就可以吐出数据并向 UI 线程发出信号,表明数据已准备就绪。然后,UI 线程可以读取中间数据并根据需要在 UI 线程上构建折线。
如果您有数千万条折线要构建并观察令人讨厌的 UI 问题,您可以将工作分成较小的批次,以避免一次阻塞 UI 太长时间。
Polyline(以及一般的 WPF)绑定到 UI 线程,并且任何后台线程都不会改变这一点。
不要以为线程会拯救你。
首先,您是否确实存在明显的 UI 阻塞问题?如果没有,你就完成了。无需穿线。
如果确实存在 UI 阻塞问题,是否可以将其分成较小的批次并仍然在 UI 线程上执行?如果是的话,你就完成了。无需穿线。
如果使用后台线程确实是解决问题的最有效方法,那么请在不使用 Polyline 等 WPF 类的情况下实现它。
Because Polyline is a WPF UIElement class, it is bound to the UI thread. If you try to access any of its properties from another thread, it will throw an exception.
Be mindful that Polyline is a display class, not a data type. You shouldn't be fiddling with WPF controls or other display elements from background threads. WPF will fight you every step of the way.
Your best bet is to perform your XPS to polyline conversion calculations using an intermediate data type of your own design. Anything but a UIElement will do. A value type struct might be appropriate. Once you've done whatever intensive computational gymnastics warrant a background thread, spit out your data and signal the UI thread that the data is good to go. The UI thread can then read the intermediate data and construct Polylines as needed on the UI thread.
If you have tens of millions of polylines to construct and observe an objectionable UI hiccup, you could break the work into smaller batches to keep from blocking the UI for too long at a time.
Polyline (and WPF in general) is bound to the UI thread, and no amount of background threading is going to change that.
Don't assume threading will save you.
First, do you actually have a demonstrable UI blocking issue? If not, you're done. No threading needed.
If you do have a UI blocking issue, can it be broken up into smaller batches and still execute on the UI thread? If yes, you're done. No threading needed.
If using a background thread really is the most effective way to solve your problem, then implement it without using WPF classes like Polyline.