mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 18:57:19 +02:00
Refactoring multithreading
1. ThreadPool -> Task 2. fix deadlock 3. remove unnecessory application.dispatcher.invoke 4. enable non-main thread access to results collection 5. Misc 6. part of #412
This commit is contained in:
@@ -86,11 +86,15 @@ namespace Wox.ViewModel
|
||||
{
|
||||
foreach (var pair in PluginManager.GetPluginsForInterface<IResultUpdated>())
|
||||
{
|
||||
var plugin = (IResultUpdated) pair.Plugin;
|
||||
var plugin = (IResultUpdated)pair.Plugin;
|
||||
plugin.ResultsUpdated += (s, e) =>
|
||||
{
|
||||
PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query);
|
||||
UpdateResultView(e.Results, pair.Metadata, e.Query);
|
||||
Task.Run(() =>
|
||||
{
|
||||
|
||||
PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query);
|
||||
UpdateResultView(e.Results, pair.Metadata, e.Query);
|
||||
}, _updateToken);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -202,7 +206,10 @@ namespace Wox.ViewModel
|
||||
menus.Add(ContextMenuPluginInfo(id));
|
||||
|
||||
ContextMenu.Clear();
|
||||
ContextMenu.AddResults(menus, id);
|
||||
Task.Run(() =>
|
||||
{
|
||||
ContextMenu.AddResults(menus, id);
|
||||
}, _updateToken);
|
||||
ContextMenuVisibility = Visibility.Visible;
|
||||
}
|
||||
else
|
||||
@@ -219,14 +226,14 @@ namespace Wox.ViewModel
|
||||
|
||||
private void InitializeResultListBox()
|
||||
{
|
||||
Results = new ResultsViewModel(_settings, _topMostRecord);
|
||||
Results = new ResultsViewModel(_settings);
|
||||
ResultListBoxVisibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
|
||||
private void InitializeContextMenu()
|
||||
{
|
||||
ContextMenu = new ResultsViewModel(_settings, _topMostRecord);
|
||||
ContextMenu = new ResultsViewModel(_settings);
|
||||
ContextMenuVisibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
@@ -413,7 +420,10 @@ namespace Wox.ViewModel
|
||||
}
|
||||
}
|
||||
ContextMenu.Clear();
|
||||
ContextMenu.AddResults(filterResults, contextMenuId);
|
||||
Task.Run(() =>
|
||||
{
|
||||
ContextMenu.AddResults(filterResults, contextMenuId);
|
||||
}, _updateToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -455,18 +465,20 @@ namespace Wox.ViewModel
|
||||
}, _updateToken);
|
||||
|
||||
var plugins = PluginManager.ValidPluginsForQuery(query);
|
||||
foreach (var plugin in plugins)
|
||||
Task.Run(() =>
|
||||
{
|
||||
var config = _settings.PluginSettings.Plugins[plugin.Metadata.ID];
|
||||
if (!config.Disabled)
|
||||
Parallel.ForEach(plugins, plugin =>
|
||||
{
|
||||
Task.Factory.StartNew(() =>
|
||||
var config = _settings.PluginSettings.Plugins[plugin.Metadata.ID];
|
||||
if (!config.Disabled)
|
||||
{
|
||||
|
||||
var results = PluginManager.QueryForPlugin(plugin, query);
|
||||
UpdateResultView(results, plugin.Metadata, query);
|
||||
}, _updateToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}, _updateToken);
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -478,11 +490,6 @@ namespace Wox.ViewModel
|
||||
_queryHistory.Reset();
|
||||
}
|
||||
|
||||
private void UpdateResultViewInternal(List<Result> list, PluginMetadata metadata)
|
||||
{
|
||||
Results.AddResults(list, metadata.ID);
|
||||
}
|
||||
|
||||
private void DisplayQueryHistory(HistoryItem history)
|
||||
{
|
||||
if (history != null)
|
||||
@@ -495,21 +502,23 @@ namespace Wox.ViewModel
|
||||
var executeQueryHistoryTitle = InternationalizationManager.Instance.GetTranslation("executeQuery");
|
||||
var lastExecuteTime = InternationalizationManager.Instance.GetTranslation("lastExecuteTime");
|
||||
Results.RemoveResultsExcept(historyMetadata);
|
||||
UpdateResultViewInternal(new List<Result>
|
||||
var result = new Result
|
||||
{
|
||||
new Result
|
||||
Title = string.Format(executeQueryHistoryTitle, history.Query),
|
||||
SubTitle = string.Format(lastExecuteTime, history.ExecutedDateTime),
|
||||
IcoPath = "Images\\history.png",
|
||||
PluginDirectory = Infrastructure.Wox.ProgramPath,
|
||||
Action = _ =>
|
||||
{
|
||||
Title = string.Format(executeQueryHistoryTitle,history.Query),
|
||||
SubTitle = string.Format(lastExecuteTime,history.ExecutedDateTime),
|
||||
IcoPath = "Images\\history.png",
|
||||
PluginDirectory = Infrastructure.Wox.ProgramPath,
|
||||
Action = _ =>{
|
||||
QueryText = history.Query;
|
||||
OnTextBoxSelected();
|
||||
return false;
|
||||
}
|
||||
QueryText = history.Query;
|
||||
OnTextBoxSelected();
|
||||
return false;
|
||||
}
|
||||
}, historyMetadata);
|
||||
};
|
||||
Task.Run(() =>
|
||||
{
|
||||
Results.AddResults(new List<Result> { result }, historyMetadata.ID);
|
||||
}, _updateToken);
|
||||
}
|
||||
}
|
||||
private Result ContextMenuTopMost(Result result)
|
||||
@@ -583,21 +592,29 @@ namespace Wox.ViewModel
|
||||
_topMostRecordStorage.Save();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To avoid deadlock, this method should not called from main thread
|
||||
/// </summary>
|
||||
public void UpdateResultView(List<Result> list, PluginMetadata metadata, Query originQuery)
|
||||
{
|
||||
_queryHasReturn = true;
|
||||
ProgressBarVisibility = Visibility.Hidden;
|
||||
|
||||
list.ForEach(o =>
|
||||
foreach (var result in list)
|
||||
{
|
||||
o.Score += _userSelectedRecord.GetSelectedCount(o) * 5;
|
||||
});
|
||||
if (_topMostRecord.IsTopMost(result))
|
||||
{
|
||||
result.Score = int.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Score += _userSelectedRecord.GetSelectedCount(result) * 5;
|
||||
}
|
||||
}
|
||||
|
||||
if (originQuery.RawQuery == _lastQuery.RawQuery)
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
UpdateResultViewInternal(list, metadata);
|
||||
});
|
||||
Results.AddResults(list, metadata.ID);
|
||||
}
|
||||
|
||||
if (list.Count > 0 && !ResultListBoxVisibility.IsVisible())
|
||||
|
||||
@@ -4,7 +4,9 @@ using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using Wox.Core.UserSettings;
|
||||
using Wox.Plugin;
|
||||
using Wox.Storage;
|
||||
@@ -16,17 +18,18 @@ namespace Wox.ViewModel
|
||||
#region Private Fields
|
||||
|
||||
private ResultViewModel _selectedResult;
|
||||
public ResultCollection Results { get; } = new ResultCollection();
|
||||
public ResultCollection Results { get; }
|
||||
private Thickness _margin;
|
||||
|
||||
private readonly object _resultsUpdateLock = new object();
|
||||
private Settings _settings;
|
||||
private TopMostRecord _topMostRecord;
|
||||
private readonly object _addResultsLock = new object();
|
||||
private readonly object _collectionLock = new object();
|
||||
private readonly Settings _settings;
|
||||
|
||||
public ResultsViewModel(Settings settings, TopMostRecord topMostRecord)
|
||||
public ResultsViewModel(Settings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
_topMostRecord = topMostRecord;
|
||||
Results = new ResultCollection();
|
||||
BindingOperations.EnableCollectionSynchronization(Results, _collectionLock);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -81,11 +84,6 @@ namespace Wox.ViewModel
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private bool IsTopMostResult(Result result)
|
||||
{
|
||||
return _topMostRecord.IsTopMost(result);
|
||||
}
|
||||
|
||||
private int InsertIndexOf(int newScore, IList<ResultViewModel> list)
|
||||
{
|
||||
int index = 0;
|
||||
@@ -181,45 +179,35 @@ namespace Wox.ViewModel
|
||||
|
||||
public void RemoveResultsExcept(PluginMetadata metadata)
|
||||
{
|
||||
lock (_resultsUpdateLock)
|
||||
{
|
||||
Results.RemoveAll(r => r.RawResult.PluginID != metadata.ID);
|
||||
}
|
||||
Results.RemoveAll(r => r.RawResult.PluginID != metadata.ID);
|
||||
}
|
||||
|
||||
public void RemoveResultsFor(PluginMetadata metadata)
|
||||
{
|
||||
lock (_resultsUpdateLock)
|
||||
{
|
||||
Results.RemoveAll(r => r.PluginID == metadata.ID);
|
||||
}
|
||||
Results.RemoveAll(r => r.PluginID == metadata.ID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To avoid deadlock, this method should not called from main thread
|
||||
/// </summary>
|
||||
public void AddResults(List<Result> newRawResults, string resultId)
|
||||
{
|
||||
lock (_resultsUpdateLock)
|
||||
lock (_addResultsLock)
|
||||
{
|
||||
var newResults = newRawResults.Select(r => new ResultViewModel(r)).ToList();
|
||||
// todo use async to do new result calculation
|
||||
var resultsCopy = Results.ToList();
|
||||
var oldResults = resultsCopy.Where(r => r.PluginID == resultId).ToList();
|
||||
|
||||
// intersection of A (old results) and B (new newResults)
|
||||
var intersection = oldResults.Intersect(newResults).ToList();
|
||||
|
||||
// remove result of relative complement of B in A
|
||||
foreach (var result in oldResults.Except(intersection))
|
||||
{
|
||||
resultsCopy.Remove(result);
|
||||
}
|
||||
|
||||
// update scores
|
||||
foreach (var result in newResults)
|
||||
{
|
||||
if (IsTopMostResult(result.RawResult))
|
||||
{
|
||||
result.Score = int.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
// update index for result in intersection of A and B
|
||||
foreach (var commonResult in intersection)
|
||||
{
|
||||
@@ -300,6 +288,7 @@ namespace Wox.ViewModel
|
||||
ResultViewModel newResult = newItems[i];
|
||||
if (!oldResult.Equals(newResult))
|
||||
{
|
||||
|
||||
this[i] = newResult;
|
||||
}
|
||||
else if (oldResult.Score != newResult.Score)
|
||||
@@ -308,6 +297,7 @@ namespace Wox.ViewModel
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (newCount > oldCount)
|
||||
{
|
||||
for (int i = oldCount; i < newCount; i++)
|
||||
@@ -323,7 +313,6 @@ namespace Wox.ViewModel
|
||||
RemoveAt(removeIndex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user