다른 스레드가 이 개체를 소유하고 있기 때문에 호출 스레드가 이 개체에 액세스할 수 없습니다.
제 코드는 아래와 같습니다.
public CountryStandards()
{
InitializeComponent();
try
{
FillPageControls();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Country Standards", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// Fills the page controls.
/// </summary>
private void FillPageControls()
{
popUpProgressBar.IsOpen = true;
lblProgress.Content = "Loading. Please wait...";
progress.IsIndeterminate = true;
worker = new BackgroundWorker();
worker.DoWork += new System.ComponentModel.DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(worker_ProgressChanged);
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.RunWorkerAsync();
}
private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
GetGridData(null, 0); // filling grid
}
private void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progress.Value = e.ProgressPercentage;
}
private void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
worker = null;
popUpProgressBar.IsOpen = false;
//filling Region dropdown
Standards.UDMCountryStandards objUDMCountryStandards = new Standards.UDMCountryStandards();
objUDMCountryStandards.Operation = "SELECT_REGION";
DataSet dsRegionStandards = objStandardsBusinessLayer.GetCountryStandards(objUDMCountryStandards);
if (!StandardsDefault.IsNullOrEmptyDataTable(dsRegionStandards, 0))
StandardsDefault.FillComboBox(cmbRegion, dsRegionStandards.Tables[0], "Region", "RegionId");
//filling Currency dropdown
objUDMCountryStandards = new Standards.UDMCountryStandards();
objUDMCountryStandards.Operation = "SELECT_CURRENCY";
DataSet dsCurrencyStandards = objStandardsBusinessLayer.GetCountryStandards(objUDMCountryStandards);
if (!StandardsDefault.IsNullOrEmptyDataTable(dsCurrencyStandards, 0))
StandardsDefault.FillComboBox(cmbCurrency, dsCurrencyStandards.Tables[0], "CurrencyName", "CurrencyId");
if (Users.UserRole != "Admin")
btnSave.IsEnabled = false;
}
/// <summary>
/// Gets the grid data.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="pageIndex">Index of the page.( used in case of paging) </pamam>
private void GetGridData(object sender, int pageIndex)
{
Standards.UDMCountryStandards objUDMCountryStandards = new Standards.UDMCountryStandards();
objUDMCountryStandards.Operation = "SELECT";
objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null;
DataSet dsCountryStandards = objStandardsBusinessLayer.GetCountryStandards(objUDMCountryStandards);
if (!StandardsDefault.IsNullOrEmptyDataTable(dsCountryStandards, 0) && (chkbxMarketsSearch.IsChecked == true || chkbxBudgetsSearch.IsChecked == true || chkbxProgramsSearch.IsChecked == true))
{
DataTable objDataTable = StandardsDefault.FilterDatatableForModules(dsCountryStandards.Tables[0], "Country", chkbxMarketsSearch, chkbxBudgetsSearch, chkbxProgramsSearch);
dgCountryList.ItemsSource = objDataTable.DefaultView;
}
else
{
MessageBox.Show("No Records Found", "Country Standards", MessageBoxButton.OK, MessageBoxImage.Information);
btnClear_Click(null, null);
}
}
»objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null;
는 예외를 .
다른 스레드가 이 개체를 소유하고 있기 때문에 호출 스레드가 이 개체에 액세스할 수 없습니다.
여기 왜 그래요?
이것은 사람들이 시작할 때 흔히 발생하는 문제입니다.메인 스레드가 아닌 스레드에서 UI 요소를 업데이트할 때마다 다음을 사용해야 합니다.
this.Dispatcher.Invoke(() =>
{
...// your code here.
});
를 사용하여 현재 스레드가 컨트롤을 소유하고 있는지 확인할 수도 있습니다.만약 그것이 그것을 소유한다면, 당신의 코드는 정상적으로 보입니다.그렇지 않으면 위의 패턴을 사용합니다.
제 2센트를 더하자면, 당신이 당신의 코드를 통해 호출하더라도 예외가 발생할 수 있습니다.System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
.
요점은 당신이 전화를 해야 한다는 것입니다.Invoke()
의 시대의Dispatcher
액세스하려는 컨트롤의 경우와 동일하지 않을 수 있습니다.System.Windows.Threading.Dispatcher.CurrentDispatcher
그래서 대신에 당신은 사용해야 합니다.YourControl.Dispatcher.Invoke()
는 이것을에 몇 치고 .저는 이것을 깨닫기 전에 몇 시간 동안 머리를 쾅쾅 치고 있었습니다.
갱신하다
향후 사용자의 경우 최신 버전의 .NET(4.0 이상)에서 변경된 것으로 보입니다.이제 VM에서 UI 지원 속성을 업데이트할 때 올바른 디스패처에 대해 더 이상 걱정할 필요가 없습니다. WPF 엔진은 올바른 UI 스레드에서 교차 스레드 호출을 마샬링합니다.자세한 내용은 여기를 참조하십시오.@aaronburro에게 정보와 링크를 주셔서 감사합니다.아래 대화 내용을 댓글로 읽어보실 수도 있습니다.
업데이트 2
지금은 인기 있는 게시물이기 때문에 앞으로 몇 년 동안 경험한 내용을 공유하려고 생각했습니다.이 동작은 크로스 스레드 호출에서 속성 바인딩이 올바르게 업데이트되는 것처럼 보입니다(마셜링이 필요하지 않으며 WPF가 대신 처리합니다).OTOH 명령 바인딩을 UI 디스패처에 위임해야 합니다.MVVM Light와 비교적 새로운 Community Toolkit을 모두 사용하여 테스트해 보았는데, 이전 프레임워크와 새로운 .NET 5 및 6이 모두 해당되는 것 같습니다.AsyncRelayCommand
가 아닌 다른 할 때 는 UI가 UI일 때 합니다).CanExecuteChanged
예를 들어, 버튼의 상태를 업데이트하는 작업자 스레드에서 실행됩니다.Enabled
시할 때 입니다.물론 솔루션은 시작 시 VM의 전역 공간에 UI 디스패처를 저장한 다음 UI를 업데이트할 때 사용하는 것입니다.
이 문제가 발생하여 작업할 때 별도의 작업자 스레드에 UI 컨트롤이 생성된 경우BitmapSource
또는ImageSource
WPF 호출, 호출Freeze()
통과하기 전에 먼저 방법BitmapSource
또는ImageSource
모든 방법에 대한 매개 변수로 사용됩니다.용사를 합니다.Application.Current.Dispatcher.Invoke()
.
내가 하려고 했기 때문에 이런 일이 일어났습니다.access UI
요소입니다.another thread insted of UI thread
이것처럼.
private void button_Click(object sender, RoutedEventArgs e)
{
new Thread(SyncProcces).Start();
}
private void SyncProcces()
{
string val1 = null, val2 = null;
//here is the problem
val1 = textBox1.Text;//access UI in another thread
val2 = textBox2.Text;//access UI in another thread
localStore = new LocalStore(val1);
remoteStore = new RemoteStore(val2);
}
이 문제를 해결하려면, Candide가 그의 답변에서 위에 언급한 내용 안에 UI 통화를 감쌉니다.
private void SyncProcces()
{
string val1 = null, val2 = null;
this.Dispatcher.Invoke((Action)(() =>
{//this refer to form in WPF application
val1 = textBox.Text;
val2 = textBox_Copy.Text;
}));
localStore = new LocalStore(val1);
remoteStore = new RemoteStore(val2 );
}
UI 스레드에서 해야 합니다.사용:
Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)}));
어떤 이유에서인지 Candide의 대답은 지어지지 않았습니다.하지만 완벽하게 작동하는 이것을 찾게 해주었기 때문에 도움이 되었습니다.
System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() =>
{
//your code here...
}));
이것은 나에게 효과가 있습니다.
new Thread(() =>
{
Thread.CurrentThread.IsBackground = false;
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate {
//Your Code here.
}, null);
}).Start();
여기서 언급했듯이,Dispatcher.Invoke
UI를 고정할 수 있습니다.사해야를 사용해야 .Dispatcher.BeginInvoke
대신.
다음은 디스패처 호출 및 검사를 단순화하는 편리한 내선 번호 클래스입니다.
샘플 사용량: (WPF 창에서 호출)
this Dispatcher.InvokeIfRequired(new Action(() =>
{
logTextbox.AppendText(message);
logTextbox.ScrollToEnd();
}));
확장 클래스:
using System;
using System.Windows.Threading;
namespace WpfUtility
{
public static class DispatcherExtension
{
public static void InvokeIfRequired(this Dispatcher dispatcher, Action action)
{
if (dispatcher == null)
{
return;
}
if (!dispatcher.CheckAccess())
{
dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
return;
}
action();
}
}
}
나는 또한 그것을 발견했습니다.System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
DotNet이 답변에 기록한 것처럼 항상 목표 제어의 파견자는 아닙니다.대한 권한이 ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜApplication.Current.Dispatcher
그리고 그것은 문제를 해결했습니다.
문제는 당신이 전화하고 있다는 것입니다.GetGridData
백그라운드 스레드에서.이 메서드는 메인 스레드에 바인딩된 여러 WPF 컨트롤에 액세스합니다.백그라운드 스레드에서 액세스하려고 하면 이 오류가 발생합니다.
는 올른스돌기위가합해니다사야용해아로바레드합을 사용해야 .SynchronizationContext.Current.Post
하지만 이 경우 당신이 하는 일의 대부분은 UI 기반인 것 같습니다.따라서 바로 UI 스레드로 돌아가서 작업을 수행하기 위해 백그라운드 스레드를 만듭니다.한 후 에 새 .
필요에 따라 이를 수행하는 방법은 분명히 다릅니다.
UI 업데이트 스레드(기본 UI 스레드가 아님)를 사용하는 한 가지 방법은 스레드가 전체 논리 처리 루프가 UI 스레드에 호출되는 루프를 시작하도록 하는 것입니다.
예:
public SomeFunction()
{
bool working = true;
Thread t = new Thread(() =>
{
// Don't put the working bool in here, otherwise it will
// belong to the new thread and not the main UI thread.
while (working)
{
Application.Current.Dispatcher.Invoke(() =>
{
// Put your entire logic code in here.
// All of this code will process on the main UI thread because
// of the Invoke.
// By doing it this way, you don't have to worry about Invoking individual
// elements as they are needed.
});
}
});
}
이를 통해 코드는 전적으로 메인 UI 스레드에서 실행됩니다.이것은 크로스 스레드 작업에 머리를 싸매는 데 어려움을 겪는 아마추어 프로그래머들에게 프로가 될 수 있습니다.그러나 더 복잡한 UI(특히 애니메이션을 수행하는 경우)를 사용하면 쉽게 사기꾼이 될 수 있습니다.실제로, 이것은 UI를 업데이트한 다음 효율적인 교차 스레드 작업 대신 발생한 모든 이벤트를 처리하는 시스템을 위장하기 위한 것입니다.
또한 다른 해결책은 컨트롤이 백그라운드 작업자 스레드가 아닌 UI 스레드에서 생성되도록 하는 것입니다.
WPF 응용 프로그램에 계단식 콤보 상자를 추가할 때 계속 오류가 발생했고 다음 API를 사용하여 오류를 해결했습니다.
using System.Windows.Data;
private readonly object _lock = new object();
private CustomObservableCollection<string> _myUiBoundProperty;
public CustomObservableCollection<string> MyUiBoundProperty
{
get { return _myUiBoundProperty; }
set
{
if (value == _myUiBoundProperty) return;
_myUiBoundProperty = value;
NotifyPropertyChanged(nameof(MyUiBoundProperty));
}
}
public MyViewModelCtor(INavigationService navigationService)
{
// Other code...
BindingOperations.EnableCollectionSynchronization(AvailableDefectSubCategories, _lock );
}
자세한 내용은 https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Windows.Data.BindingOperations.EnableCollectionSynchronization);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.7);k(DevLang-csharp)&rd=true 를 참조하십시오.
가끔은 제가 분명히 보고 있던 대상이 아니라 예외를 던지는 것이 당신이 만든 개체일 수도 있습니다.
내 코드는 다음과 같습니다.
xaml 파일:
<Grid Margin="0,0,0,0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" >
<TextBlock x:Name="tbScreenLog" VerticalAlignment="Stretch" Background="Black" FontSize="12" Foreground="#FF919191" HorizontalAlignment="Stretch"/>
</Grid>
xaml.cs 파일:
System.Windows.Documents.Run rnLine = new System.Windows.Documents.Run(Message.Item2 + "\r\n");
rnLine.Foreground = LineAlternate ? Brushes.Green : Brushes.Orange;
Dispatcher.Invoke(()=> {
tbScreenLog.Inlines.Add(rnLine);
});
LineAlternate = !LineAlternate;
다른 스레드에서 개체에 액세스하는 것에 대한 예외를 얻었지만 UI 스레드에서 호출했습니다.
잠시 후 그것은 TextBlock 객체가 아니라 호출하기 전에 만든 Run 객체에 대한 것이라는 것이 저를 위축시켰습니다.
코드를 이것으로 변경하여 문제가 해결되었습니다.
Dispatcher.Invoke(()=> {
Run rnLine = new Run(Message.Item2 + "\r\n");
rnLine.Foreground = LineAlternate ? Brushes.Green : Brushes.Orange;
tbScreenLog.Inlines.Add(rnLine);
});
LineAlternate = !LineAlternate;
WPF 컨트롤에서 선택한 두 번째 항목에서 이 오류가 발생했습니다.
그 이유는 데이터를 RX 소스 캐시에 로드했고 로드된 요소에는 Collection View로 포장된 ObservableCollections(관찰 가능한 Collections)가 Navigation Properties(탐색 가능한 Collections)로 포함되어 있었기 때문입니다.관찰 가능한 컬렉션이 UI 스레드에 연결되고 작업자 스레드에 의해 데이터가 로드되었습니다.CollectionView가 첫 번째 요소를 표시할 때만 채워졌기 때문에 다른 스레드의 문제는 선택된 두 번째 항목에서만 발생했습니다.
솔루션은 하위 목록을 View Model로 ReadOnlyObservableCollections로 이동하고 현재 선택한 주 요소를 기준으로 하위 요소 테이블의 전체 목록을 필터링하는 것입니다.
언급URL : https://stackoverflow.com/questions/9732709/the-calling-thread-cannot-access-this-object-because-a-different-thread-owns-it
'programing' 카테고리의 다른 글
C++에서 벡터를 초기화하는 방법 (0) | 2023.05.28 |
---|---|
nodejs 및 express로 만든 REST API에서 응답 상태 및 JSON 내용을 올바르게 설정하는 방법 (0) | 2023.05.28 |
별도의 줄에서 %path% 에코 (0) | 2023.05.28 |
Xcode: 장치 지원 파일을 찾을 수 없습니다. (0) | 2023.05.28 |
.NET WebBrowser 컨트롤을 Chrome과 같은 더 나은 브라우저로 대체하시겠습니까? (0) | 2023.05.28 |