2015-01-24 2 views
0

В моем приложении, когда Button нажатии на Command запускается, что загружает image (асинхронно .. я надеюсь) и strings, name и character, указанные ниже. Функция для загрузки этих элементов вызывается из DispatcherTimer как таковой:Are Timer and Task создает новые темы?

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'scastInfoWithPics для хранения значения image.

Именно в этот момент, что я получаю ошибку:

The calling thread cannot access this object because a different thread owns it. 

SelectedItem объявлен в моем ViewModel.

SelectedItem Класс

public List<CastWithPic> castInfoWithPics { get; set; } 

CastWithPic Класс

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(); 

Мой вопрос:

Почему я не получаю ошибку при назначении _viewModel.SelectedItem.castInfoWithPics = castWithPicList; в методе timer, но получаю ошибку в методе Task.

Разве не timer также есть новый thread?

В этом случае это правильный способ решить проблему, чтобы использовать предлагаемое здесь решение?

The calling thread cannot access this object because a different thread owns it

+0

Какая у вас функция 'downloadBitmap'? –

+0

Привет @NedStoyanov, я добавил функцию 'downloadBitmap'. –

+0

Где вы устанавливаете 'SelectedItem' это' RspCallback'? –

ответ

1

Думаю, что _viewModel.SelectedItem.castInfoWithPics = castWithPicList должен быть установлен в потоке пользовательского интерфейса. Ваш donloadBitmap funciton использует обратный вызов для обработки ответа и работает в потоке ThreadPool.

Вы можете использовать Dispatcher.Invoke, чтобы заставить работу возникнуть в потоке пользовательского интерфейса.Попробуйте положить это в обратный вызов и поставить соответствующий код в комментировал разделе:

Application.Current.Dispatcher.BeginInvoke( 
    DispatcherPriority.Background, 
    new Action(() => /* set selected item */)); 

Для лучшего решения, которое вы можете использовать async-await, который восстанавливает текущий SynchronizatonContext и лет не нужно использовать Dispatcher.Invoke. HttpClient предоставляет асинхронный API, который вы можете использовать.

Вот простой пример, устанавливающий значение TextBox.

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

Это хорошая догадка. Трудно сказать, когда OP не предоставил необходимые данные, например, какой объект таймера он использует или что такое сообщение об ошибке. –

-2

Вы должны заморозить объект и проблема решена. Вот пример:

videoIcon = new BitmapImage(); 
videoIcon.BeginInit(); 
videoIcon.UriSource = new Uri(@"C:\Data\Image1.jpg"); 
videoIcon.DecodePixelWidth = 14; 
videoIcon.EndInit(); 
videoIcon.Freeze(); 

Без заморозка() я получил то же самое сообщение об ошибке вы получили. С помощью Freeze() проблема решена.
Подробнее см. Здесь: https://msdn.microsoft.com/en-us/library/ms750509(v=vs.110).aspx
«Замороженный замораживаемый файл можно также делиться между потоками, в то время как незамороженный Freezable не может».

+0

Я не думаю, что это имеет какое-то отношение к проблеме OP. –

+0

Роберт Харви, вы, кажется, экспорт, и, возможно, вы правы. Но у меня была аналогичная проблема, и после значительных исследований я нашел решение Freeze(), которое сработало для меня. Возможно, Гири может попытаться заморозить изображения и сообщить нам, если это решает проблему. – Edgar