Timer 和 Task 是否正在创建新线程?

Are Timer and Task creating new threads?

在我的应用程序中,当单击 Button 时,会触发 Command 以下载 image(我希望是异步的)和 stringsnamecharacter,具体如下。下载这些项目的函数是从 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.InvokeHttpClient 提供您可以使用的异步友好 API。

这是一个设置文本框值的简单示例。

 private async Task DownloadUrlAsync(string url)
 {
    using (HttpClient httpClient = new HttpClient())
    {
        textBox1.Text = await httpClient.GetStringAsync(url);
    }            
 }