Timer 和 Task 是否正在创建新线程?
Are Timer and Task creating new threads?
在我的应用程序中,当单击 Button
时,会触发 Command
以下载 image
(我希望是异步的)和 strings
,name
和 character
,具体如下。下载这些项目的函数是从 DispatcherTimer
中调用的:
timer.Tick += myMethodToDownloadCastInfo;
在这个方法中我有:
timer.Stop();
List<CastWithPic> castWithPicList = new List<CastWithPic>();
// Add name and id for each CastWithPic in the list
// ..
_viewModel.SelectedItem.castInfoWithPics = castWithPicList;
// Download all the required pics
var tasks = new List<Task>();
foreach (CastWithPic cast in castWithPicList)
{
tasks.Add(downloadBitmap(cast.profilePath, cast.id));
}
await Task.WhenAll(tasks);
在 downloadBitmap
函数中,我将对象 SelectedItem's
castInfoWithPics
设置为保存 image
值。
此时我收到错误:
The calling thread cannot access this object because a different thread owns it.
SelectedItem
在我的 ViewModel
.
中声明
SelectedItem
Class
public List<CastWithPic> castInfoWithPics { get; set; }
CastWithPic
Class
public string name
{
get { return (string)this.GetValue(nameProperty); }
set
{
this.SetValue(nameProperty, value);
}
}
public static readonly DependencyProperty nameProperty = DependencyProperty.Register(
"name", typeof(string),
typeof(CastWithPic));
public string character
{
get { return (string)this.GetValue(characterProperty); }
set
{
this.SetValue(characterProperty, value);
}
}
public static readonly DependencyProperty characterProperty = DependencyProperty.Register(
"character", typeof(string),
typeof(CastWithPic));
public Bitmap image
{
get { return (Bitmap)this.GetValue(imageProperty); }
set
{
this.SetValue(imageProperty, value);
}
}
public static readonly DependencyProperty imageProperty = DependencyProperty.Register(
"image", typeof(Bitmap),
typeof(CastWithPic));
downloadBitmap
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
RequestState myRequestState = new RequestState();
myRequestState.request = request;
// Start the asynchronous request.
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(RespCallback), Tuple.Create(myRequestState, actorID));
// this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, DefaultTimeout, true);
// The response came in the allowed time. The work processing will happen in the
// callback function.
allDone.WaitOne();
我的问题是:
为什么我在 timer
方法中赋值 _viewModel.SelectedItem.castInfoWithPics = castWithPicList;
时没有出错,但在 Task
方法中却出错。
timer
不也开始新的thread
了吗?
在这种情况下,使用此处建议的解决方案是解决问题的正确方法吗?
The calling thread cannot access this object because a different thread owns it
你必须冻结你的对象,问题就解决了。
这是一个示例:
videoIcon = new BitmapImage();
videoIcon.BeginInit();
videoIcon.UriSource = new Uri(@"C:\Data\Image1.jpg");
videoIcon.DecodePixelWidth = 14;
videoIcon.EndInit();
videoIcon.Freeze();
在没有 Freeze() 的情况下,我收到了与您收到的相同的错误消息。使用 Freeze() 问题就解决了。
更多信息在这里:
https://msdn.microsoft.com/en-us/library/ms750509(v=vs.110).aspx
"A frozen Freezable can also be shared across threads, while an unfrozen Freezable cannot."
我认为_viewModel.SelectedItem.castInfoWithPics = castWithPicList
需要在UI线程中设置。您的 donloadBitmap
函数使用回调来处理响应并在 ThreadPool
线程上运行。
您可以使用 Dispatcher.Invoke
强制在 UI 线程上进行工作。尝试将其放入您的回调中,并将适当的代码放入评论部分:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => /* set selected item */));
为了更好的解决方案,您可以使用 async-await
来恢复当前的 SynchronizatonContext
而您不需要使用 Dispatcher.Invoke
。 HttpClient
提供您可以使用的异步友好 API。
这是一个设置文本框值的简单示例。
private async Task DownloadUrlAsync(string url)
{
using (HttpClient httpClient = new HttpClient())
{
textBox1.Text = await httpClient.GetStringAsync(url);
}
}
在我的应用程序中,当单击 Button
时,会触发 Command
以下载 image
(我希望是异步的)和 strings
,name
和 character
,具体如下。下载这些项目的函数是从 DispatcherTimer
中调用的:
timer.Tick += myMethodToDownloadCastInfo;
在这个方法中我有:
timer.Stop();
List<CastWithPic> castWithPicList = new List<CastWithPic>();
// Add name and id for each CastWithPic in the list
// ..
_viewModel.SelectedItem.castInfoWithPics = castWithPicList;
// Download all the required pics
var tasks = new List<Task>();
foreach (CastWithPic cast in castWithPicList)
{
tasks.Add(downloadBitmap(cast.profilePath, cast.id));
}
await Task.WhenAll(tasks);
在 downloadBitmap
函数中,我将对象 SelectedItem's
castInfoWithPics
设置为保存 image
值。
此时我收到错误:
The calling thread cannot access this object because a different thread owns it.
SelectedItem
在我的 ViewModel
.
SelectedItem
Class
public List<CastWithPic> castInfoWithPics { get; set; }
CastWithPic
Class
public string name
{
get { return (string)this.GetValue(nameProperty); }
set
{
this.SetValue(nameProperty, value);
}
}
public static readonly DependencyProperty nameProperty = DependencyProperty.Register(
"name", typeof(string),
typeof(CastWithPic));
public string character
{
get { return (string)this.GetValue(characterProperty); }
set
{
this.SetValue(characterProperty, value);
}
}
public static readonly DependencyProperty characterProperty = DependencyProperty.Register(
"character", typeof(string),
typeof(CastWithPic));
public Bitmap image
{
get { return (Bitmap)this.GetValue(imageProperty); }
set
{
this.SetValue(imageProperty, value);
}
}
public static readonly DependencyProperty imageProperty = DependencyProperty.Register(
"image", typeof(Bitmap),
typeof(CastWithPic));
downloadBitmap
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
RequestState myRequestState = new RequestState();
myRequestState.request = request;
// Start the asynchronous request.
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(RespCallback), Tuple.Create(myRequestState, actorID));
// this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, DefaultTimeout, true);
// The response came in the allowed time. The work processing will happen in the
// callback function.
allDone.WaitOne();
我的问题是:
为什么我在 timer
方法中赋值 _viewModel.SelectedItem.castInfoWithPics = castWithPicList;
时没有出错,但在 Task
方法中却出错。
timer
不也开始新的thread
了吗?
在这种情况下,使用此处建议的解决方案是解决问题的正确方法吗?
The calling thread cannot access this object because a different thread owns it
你必须冻结你的对象,问题就解决了。 这是一个示例:
videoIcon = new BitmapImage();
videoIcon.BeginInit();
videoIcon.UriSource = new Uri(@"C:\Data\Image1.jpg");
videoIcon.DecodePixelWidth = 14;
videoIcon.EndInit();
videoIcon.Freeze();
在没有 Freeze() 的情况下,我收到了与您收到的相同的错误消息。使用 Freeze() 问题就解决了。
更多信息在这里:
https://msdn.microsoft.com/en-us/library/ms750509(v=vs.110).aspx
"A frozen Freezable can also be shared across threads, while an unfrozen Freezable cannot."
我认为_viewModel.SelectedItem.castInfoWithPics = castWithPicList
需要在UI线程中设置。您的 donloadBitmap
函数使用回调来处理响应并在 ThreadPool
线程上运行。
您可以使用 Dispatcher.Invoke
强制在 UI 线程上进行工作。尝试将其放入您的回调中,并将适当的代码放入评论部分:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => /* set selected item */));
为了更好的解决方案,您可以使用 async-await
来恢复当前的 SynchronizatonContext
而您不需要使用 Dispatcher.Invoke
。 HttpClient
提供您可以使用的异步友好 API。
这是一个设置文本框值的简单示例。
private async Task DownloadUrlAsync(string url)
{
using (HttpClient httpClient = new HttpClient())
{
textBox1.Text = await httpClient.GetStringAsync(url);
}
}