C# 如何从并发运行的任务更新进度栏
我正在尝试将并行任务绑定到包含pprogressBars的listView。 我使用的是一个只允许指定最大并行度的有限调度器。 到目前为止,它在大多数情况下工作正常,但偶尔两个任务会更新listView中的同一进度条。 下面是我的代码 知道如何防止两个任务更新listView中的同一进度条吗? 或者如何从并发运行的任务更新进度条C# 如何从并发运行的任务更新进度栏,c#,progress-bar,task-parallel-library,C#,Progress Bar,Task Parallel Library,我正在尝试将并行任务绑定到包含pprogressBars的listView。 我使用的是一个只允许指定最大并行度的有限调度器。 到目前为止,它在大多数情况下工作正常,但偶尔两个任务会更新listView中的同一进度条。 下面是我的代码 知道如何防止两个任务更新listView中的同一进度条吗? 或者如何从并发运行的任务更新进度条 public class MyClass { public ObservableCollection<StatusInfo> StatusItems {
public class MyClass
{
public ObservableCollection<StatusInfo> StatusItems { get; set; }
private Object thisLock = new Object();
public int Process() //handled
{
StatusItems = new ObservableCollection<StatusInfo>();
for (int i = 0; i < 4; i++) // initialize progress bar collection
{
StatusInfo sInfo = new StatusInfo();
sInfo.ThreadID = i;
sInfo.Table = "No Table";
sInfo.Percentage = 0;
sInfo.Status = AppInfo.AVAILABLE;
sInfo.Minimum = 0;
sInfo.Maximum = 100;
sInfo.Visibility = Visibility.Visible;
StatusItems.Add(sInfo);
}
Parent.StatusItems = StatusItems; // assign to viewmodel
int numberOfBackGroundThread = 4;
LimitedTaskScheduler scheduler = new LimitedTaskScheduler(numberOfBackGroundThread);
TaskFactory factory = new TaskFactory(scheduler);
var ui = LimitedTaskScheduler.FromCurrentSynchronizationContext();
Task[] tasks = new Task[rows.Count];
for (int i = 0; i < rows.Count; i++)
{
......
tasks[i] = factory.StartNew<string>(() =>
{
int barIndex = -1;
int threadID = Thread.CurrentThread.ManagedThreadId;
cnt++;
if (cnt > numberOfBackGroundThread - 1)
{
while (true)
{
for (int j = 0; j < numberOfBackGroundThread; j++)
{
lock (thisLock)
{
if (StatusItems[j].Status == "AVAILABLE" || StatusItems[j].Status == "DONE")
{
//Console.WriteLine(String.Format("Current table = {0}, Cuurent table type = {1}, BarIndex = {2}, ThreadID = {3}, Prev TableName = {4}, Prev TableType = {5}, Prev Status = {6} PrevThreadID = {7}", tabName, tabType, j.ToString(), Thread.CurrentThread.ManagedThreadId.ToString(), StatusItems[j].Table, StatusItems[j].TabType, StatusItems[j].Status, StatusItems[j].ThreadID));
// 0. Assign the current task to a display slot StatusItems[j] that is currently free.
// 1. We lock this piece of code to prevent another concurrent task from picking the same slot.
// 2. And we update the slot as "Occupied" immediately to avoid to prevent another concurrent task from picking the same slot.
// 3. We also add one extra slot to avoid concurrent tasks competing on the same sidplay slot and reduce wait on the lock.
// All of the above cannot completely avoid two thread using the same display slot. We may need a new way to
// 4. Since multiple tasks may run on the same thread we may see the same thread ID appear on different display slots.
StatusItems[j].Status = "Occupied";
barIndex = j;
break; // break for loop
}
}
}
if (barIndex >= 0) { break; } // break while loop
}
}
else { barIndex = cnt; }
StatusItems[barIndex].TabType = tabType;
StatusItems[barIndex].ThreadID = threadID;
int nStatus = IndividualProcess(barIndex);
if (nStatus < 0) { AppInfo.JobStatus = "01"; }
return result;
});
}
var done = factory.ContinueWhenAll(tasks, completedTasks => { AppInfo.Finished = true; });
done.ContinueWith(completedTasks => { int nStatus = PostProcess(); }, ui);
return returnStatus;
}
private int IndividualProcess(int barIndex)
{
for (int i=0; i< 100; i++)
{
perform work...
SetProgressbar (i, StatusItems, barIndex, "in progress")
}
SetProgressbar (100, StatusItems, barIndex, "DONE")
}
public void SetProgressbar(int pPercent, ObservableCollection<StatusInfo> pInfo, int pInfoIndex, string pStatus)
{
try // Increment percentage for COPY or nested PURGE
{
if (Application.Current.Dispatcher.Thread != System.Threading.Thread.CurrentThread)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
((StatusInfo)pInfo[pInfoIndex]).Percentage = pPercent;
((StatusInfo)pInfo[pInfoIndex]).Status = pStatus;
((StatusInfo)pInfo[pInfoIndex]).PCT = pPercent.ToString() + "%";
}));
}
else // When the current thread is main UI thread. The label won't be updated until the EntityCopy() finishes.
{
((StatusInfo)pInfo[pInfoIndex]).Percentage = pPercent;
((StatusInfo)pInfo[pInfoIndex]).Status = pStatus;
((StatusInfo)pInfo[pInfoIndex]).PCT = pPercent.ToString() + "%";
}
}
catch { throw; }
}
}
public class LimitedTaskScheduler : TaskScheduler
{
// Fields
// Whether the current thread is processing work items.
[ThreadStatic]
private static bool _currentThreadIsProcessingItems;
// The list of tasks to be executed.
private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); // protected by lock(_tasks)
/// <summary>The maximum concurrency level allowed by this scheduler.</summary>
private readonly int _maxDegreeOfParallelism;
/// <summary>Whether the scheduler is currently processing work items.</summary>
private int _delegatesQueuedOrRunning = 0; // protected by lock(_tasks)
/// <summary>
/// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the
/// specified degree of parallelism.
/// </summary>
/// <param name="maxDegreeOfParallelism">The maximum degree of parallelism provided by this scheduler.</param>
public LimitedTaskScheduler(int maxDegreeOfParallelism)
{
if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism");
_maxDegreeOfParallelism = maxDegreeOfParallelism;
}
/// <summary>Queues a task to the scheduler.</summary>
/// <param name="task">The task to be queued.</param>
protected sealed override void QueueTask(Task task)
{
// Add the task to the list of tasks to be processed. If there aren't enough
// delegates currently queued or running to process tasks, schedule another.
lock (_tasks)
{
_tasks.AddLast(task);
if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism)
{
++_delegatesQueuedOrRunning;
NotifyThreadPoolOfPendingWork();
}
}
}
/// <summary>
/// Informs the ThreadPool that there's work to be executed for this scheduler.
/// </summary>
private void NotifyThreadPoolOfPendingWork()
{
ThreadPool.UnsafeQueueUserWorkItem(_ =>
{
// Note that the current thread is now processing work items.
// This is necessary to enable inlining of tasks into this thread.
_currentThreadIsProcessingItems = true;
try
{
// Process all available items in the queue.
while (true)
{
Task item;
lock (_tasks)
{
// When there are no more items to be processed,
// note that we're done processing, and get out.
if (_tasks.Count == 0)
{
--_delegatesQueuedOrRunning;
break;
}
// Get the next item from the queue
item = _tasks.First.Value;
_tasks.RemoveFirst();
}
// Execute the task we pulled out of the queue
base.TryExecuteTask(item);
}
}
// We're done processing items on the current thread
finally { _currentThreadIsProcessingItems = false; }
}, null);
}
/// <summary>Attempts to execute the specified task on the current thread.</summary>
/// <param name="task">The task to be executed.</param>
/// <param name="taskWasPreviouslyQueued"></param>
/// <returns>Whether the task could be executed on the current thread.</returns>
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// If this thread isn't already processing a task, we don't support inlining
if (!_currentThreadIsProcessingItems) return false;
// If the task was previously queued, remove it from the queue
if (taskWasPreviouslyQueued) TryDequeue(task);
// Try to run the task.
return base.TryExecuteTask(task);
}
/// <summary>Attempts to remove a previously scheduled task from the scheduler.</summary>
/// <param name="task">The task to be removed.</param>
/// <returns>Whether the task could be found and removed.</returns>
protected sealed override bool TryDequeue(Task task)
{
lock (_tasks) return _tasks.Remove(task);
}
/// <summary>Gets the maximum concurrency level supported by this scheduler.</summary>
public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } }
/// <summary>Gets an enumerable of the tasks currently scheduled on this scheduler.</summary>
/// <returns>An enumerable of the tasks currently scheduled.</returns>
protected sealed override IEnumerable<Task> GetScheduledTasks()
{
bool lockTaken = false;
try
{
Monitor.TryEnter(_tasks, ref lockTaken);
if (lockTaken) return _tasks.ToArray();
else throw new NotSupportedException();
}
finally
{
if (lockTaken) Monitor.Exit(_tasks);
}
}
}
公共类MyClass
{
公共ObservableCollection状态项{get;set;}
私有对象thisLock=新对象();
公共int进程()//已处理
{
StatusItems=新的ObservableCollection();
for(int i=0;i<4;i++)//初始化进度条集合
{
StatusInfo sInfo=新的StatusInfo();
sInfo.ThreadID=i;
sInfo.Table=“无表”;
sInfo.百分比=0;
sInfo.Status=AppInfo.AVAILABLE;
sInfo.最小值=0;
sInfo.最大值=100;
sInfo.Visibility=可见性.Visibility;
StatusItems.Add(sInfo);
}
Parent.StatusItems=StatusItems;//分配给viewmodel
int numberOfBackGroundThread=4;
LimitedTaskScheduler调度程序=新的LimitedTaskScheduler(numberOfBackGroundThread);
TaskFactory=新的TaskFactory(调度程序);
var ui=LimitedTaskScheduler.FromCurrentSynchronizationContext();
Task[]tasks=新任务[rows.Count];
for(int i=0;i
{
int-barIndex=-1;
int threadID=Thread.CurrentThread.ManagedThreadId;
cnt++;
如果(cnt>numberOfBackGroundThread-1)
{
while(true)
{
对于(int j=0;j=0){break;}//在循环期间中断
}
}
else{barIndex=cnt;}
StatusItems[barIndex].TabType=TabType;
StatusItems[barIndex].ThreadID=ThreadID;
int nStatus=个体过程(barIndex);
如果(nStatus<0){AppInfo.JobStatus=“01”;}
返回结果;
});
}
var done=factory.ContinueWhenAll(tasks,completedTasks=>{AppInfo.Finished=true;});
ContinueWith(completedTasks=>{int nStatus=PostProcess();},ui);
返回状态;
}
私有int IndividualProcess(int barIndex)
{
对于(int i=0;i<100;i++)
{
执行工作。。。
SetProgressbar(i、StatusItems、barIndex,“进行中”)
}
SetProgressbar(100,StatusItems,barIndex,“完成”)
}
public void SetProgressbar(int-pPercent、observetecollection pInfo、int-pInfoIndex、string-pStatus)
{
尝试//复制或嵌套清除的增量百分比
{
if(Application.Current.Dispatcher.Thread!=System.Threading.Thread.CurrentThread)
{
Application.Current.Dispatcher.BeginInvoke(新操作(()=>
{
((状态信息)pInfo[pInfoIndex])。百分比=百分之一百;
((StatusInfo)pInfo[pInfoIndex])。Status=pStatus;
((StatusInfo)pInfo[pInfoIndex]).PCT=pPercent.ToString()+“%”;
}));
}
else//当当前线程是主UI线程时。在EntityCopy()完成之前不会更新标签。
{
((状态信息)pInfo[pInfoIndex])。百分比=百分之一百;
((StatusInfo)pInfo[pInfoIndex])。Status=pStatus;
((StatusInfo)pInfo[pInfoIndex]).PCT=pPercent.ToString()+“%”;
}
}
接住{throw;}
}
}
公共类LimitedTaskScheduler:TaskScheduler
{
//田地
//当前线程是否正在处理工作项。
[线程静态]
私有静态bool\u currentThreadIsProcessingItems;
//要执行的任务的列表。
私有只读LinkedList _tasks=new LinkedList();//受锁保护(_tasks)
///此计划程序允许的最大并发级别。
私有只读int_maxDegreeOfParallelism;
///计划程序当前是否正在处理工作项。
private int _delegatesquedorrunning=0;//受锁保护(_任务)
///
///使用初始化LimitedConcurrencyLevel TaskScheduler类的实例
///指定的平行度。
///
///此计划程序提供的最大并行度。
公共有限任务调度程序(int MAXDEGEEOFPARALLELISM)
{
如果(maxDegreeOfParallelism<1)抛出
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TaskProgress
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private async void Form1_Load(object sender, EventArgs e)
{
await DoWorkAsync();
}
const int MAX_PARALLEL = 4;
readonly object _lock = new Object();
readonly SemaphoreSlim _semaphore =
new SemaphoreSlim(initialCount: MAX_PARALLEL);
HashSet<Task> _pendingTasks;
Queue<ProgressBar> _availableProgressBars;
// do all Task
async Task DoWorkAsync()
{
_availableProgressBars = new Queue<ProgressBar>();
_pendingTasks = new HashSet<Task>();
var progressBars = new ProgressBar[] {
this.progressBar1,
this.progressBar2,
this.progressBar3,
this.progressBar4 };
foreach (var item in progressBars)
_availableProgressBars.Enqueue(item);
for (int i = 0; i < 50; i++) // start 50 tasks
QueueTaskAsync(DoTaskAsync());
await Task.WhenAll(WithLock(() => _pendingTasks.ToArray()));
}
// do a sigle Task
readonly Random _random = new Random(Environment.TickCount);
async Task DoTaskAsync()
{
await _semaphore.WaitAsync();
try
{
var progressBar = WithLock(() => _availableProgressBars.Dequeue());
try
{
progressBar.Maximum = 100;
progressBar.Value = 0;
IProgress<int> progress =
new Progress<int>(value => progressBar.Value = value);
await Task.Run(() =>
{
// our simulated work takes no more than 10s
var sleepMs = _random.Next(10000) / 100;
for (int i = 0; i < 100; i++)
{
Thread.Sleep(sleepMs); // simulate work item
progress.Report(i);
}
});
}
finally
{
WithLock(() => _availableProgressBars.Enqueue(progressBar));
}
}
finally
{
_semaphore.Release();
}
}
// Add/remove a task to the list of pending tasks
async void QueueTaskAsync(Task task)
{
WithLock(() => _pendingTasks.Add(task));
try
{
await task;
}
catch
{
if (!task.IsCanceled && !task.IsFaulted)
throw;
return;
}
WithLock(() => _pendingTasks.Remove(task));
}
// execute func inside a lock
T WithLock<T>(Func<T> func)
{
lock (_lock)
return func();
}
// execute action inside a lock
void WithLock(Action action)
{
lock (_lock)
action();
}
}
}