这与我的其他问题How to cancel background printing有关。
我试图更好地理解CancellationTokenSource模型以及如何跨线程边界使用它。
我有一个主窗口(在UI线程上)后面的代码所在的位置:
public MainWindow()
{
InitializeComponent();
Loaded += (s, e) => {
DataContext = new MainWindowViewModel();
Closing += ((MainWindowViewModel)DataContext).MainWindow_Closing;
};
}
在关闭时正确调用CloseWindow代码:
private void CloseWindow(IClosable window)
{
if (window != null)
{
windowClosingCTS.Cancel();
window.Close();
}
}
通过选择菜单项,在后台线程上创建第二个窗口:
// Print Preview
public static void PrintPreview(FixedDocument fixeddocument, CancellationToken ct)
{
// Was cancellation already requested?
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
...............................
// Use my custom document viewer (the print button is removed).
var previewWindow = new PrintPreview(fixedDocumentSequence);
//Register the cancellation procedure with the cancellation token
ct.Register(() =>
previewWindow.Close()
);
previewWindow.ShowDialog();
}
}
在MainWindowViewModel(在UI线程上),我把:
public CancellationTokenSource windowClosingCTS { get; set; }
使用其构造函数:
// Constructor
public MainMenu()
{
readers = new List<Reader>();
CloseWindowCommand = new RelayCommand<IClosable>(this.CloseWindow);
windowClosingCTS = new CancellationTokenSource();
}
现在我的问题。当关闭UI线程上的MainWindow时,windowClosingCTS.Cancel()会立即调用使用ct注册的委托,即 previewWindow.Close()被调用。 现在立即返回&#34;如果(Windows!= null):
&#34;调用线程因为不同而无法访问此对象 线程拥有它。&#34;
那么我做错了什么?
答案 0 :(得分:4)
您的问题是您的预览窗口在另一个线程上运行。当您触发取消时,您在该线程上执行取消令牌的注册操作,而不是在运行预览的线程上执行。
在这些情况下,黄金标准是不使用两个UI线程。这通常会带来麻烦,而处理它们所需的工作通常是不值得的。
如果您想继续使用您的解决方案,或者想要从后台线程触发取消,您必须将您的关闭操作封送到您的窗口打开的线程中:
var initDataTable = function (selector) {
$.when(App.availableViewsXhr).done(function () {
$.ajax({
url: "api/v1.0/views/" + App.activeView + "/metadata",
type: "GET"
})
.done(function (data) {
if (App.dataTable) {
App.dataTable.destroy();
$(selector).html('');
}
appendHeaderAndFooter(selector, data.columns);
var dom = "<'row'<'col-sm-7'AEOBF><'col-sm-5'f>>";
dom += "<'row'<'#notice.col-sm-12'>>";
dom += "<'row'<'col-sm-12'tr>>";
dom += "<'row' <'col-sm-12'lip>>";
App.dataTable = $(selector).DataTable({
"lengthMenu": [[25, 50, 100], ['25', '50', '100']],
"dom": dom,
"buttons": [
{
className: 'btn-sm',
extend: 'colvis',
columns: getHideableColumnNumbers(data.columns)
}
],
"stateSave": true,
"colReorder": {
"realtime": false,
"fixedColumnsLeft": data.config.colReorder.fixedColumnsLeft
},
"order": [[getFirstOrderableColumnNumber(data.columns), "desc"]],
"columns": getColumns(data.columns, data.config),
"serverSide": true,
"ajax": {
"url": 'api/v1.0/views/' + App.activeView,
"type": "POST",
"complete": function () {
App.dataTable.columns.adjust();
}
},
"scrollX": true,
stateSaveCallback: function (settings, data) {
console.log('save');
},
stateLoadCallback: function (settings) {
console.log('load');
}
});
});
});
};
答案 1 :(得分:3)
您的代码存在问题
通过选择菜单项,在a上创建第二个窗口 后台主题:
// Print Preview
public static void PrintPreview(FixedDocument fixeddocument, CancellationToken ct)
{
// Was cancellation already requested?
if (ct.IsCancellationRequested)
ct.ThrowIfCancellationRequested();
...............................
// Use my custom document viewer (the print button is removed).
var previewWindow = new PrintPreview(fixedDocumentSequence);
//Register the cancellation procedure with the cancellation token
ct.Register(() =>
previewWindow.Close()
);
previewWindow.ShowDialog();
}
}
我认为是
Task.Run(() => PrintPreview(foo, cancel));
正确的解决方案是在一个线程上完成所有事情。
public static Task<bool> PrintPreview(FixedDocument fixeddocument, CancellationToken ct)
{
var tcs = new TaskCompletionSource<bool>();
// Was cancellation already requested?
if (ct.IsCancellationRequested)
tcs.SetResult(false);
else
{
// Use my custom document viewer (the print button is removed).
var previewWindow = new PrintPreview(fixedDocumentSequence);
//Register the cancellation procedure with the cancellation token
ct.Register(() => previewWindow.Close());
previewWindow.Closed += (o, e) =>
{
var result = previewWindow.DialogResult;
if (result.HasValue)
tcs.SetResult(result.Value);
else
tcs.SetResult(false);
}
previewWindow.Show();
}
return tcs.Task;
}
然后致电
var shouldPrint = await PrintPreview(foo, cancel);
if (shouldPrint)
await PrintAsync(foo);