Given this code below
var array = new object[10];
for(int x = 0;x<array.Length;x++)
array[x] = new object();
//Lock on Array
lock(array){
//Do Stuff
}
//Lock on object of array
lock(array[1]){
//Do Stuff
}
//lock on another object
var o = array[1];
lock(o){
//Do Stuff
}
The first lock statement will lock on the object array.
But on the second lock statement, is locking happening on the object at index 1 of the array, or is it also happening on the object array? Another way of asking the question, are the 2nd lock and the 3rd lock the same behavior?
I've never liked working with lock because of the complexity around multithreading. Answering my question, using the following code here are the results.
Scenrio 2 and 3 are equivalent. Those lock statements interfere with each other (this is what one would expect by looking at the code).
Scenario 1 doesn't interfere with 2 or 3.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
public static object o;
public static object[] array;
static void Main(string[] args)
{
array = new object[10];
for (int x = 0; x < array.Length; x++)
array[x] = new object();
o = array[1];
var tasks = new Task[100];
Task t;
//t = Task.Run(() => lockArray(5000));
t = Task.Run(() => lockArrayIndex(5000));
//t = Task.Run(() => lockObject(5000));
for (int i = 0; i < tasks.Length; i++)
{
//tasks[i] = Task.Run(() => lockArray(1000));
//tasks[i] = Task.Run(() => lockArrayIndex(1000));
tasks[i] = Task.Run(() => lockObject(1000));
}
Task.WaitAll(tasks);
"done".Dump();
Console.ReadKey();
}
private static void lockArray(int input)
{
//Lock on Array
lock (array)
{
System.Threading.Thread.Sleep(input);
"Array".Dump();
}
}
private static void lockArrayIndex(int input)
{
//Lock on object of array
lock (array[1])
{
System.Threading.Thread.Sleep(input);
"Array[1]".Dump();
}
}
private static void lockObject(int input)
{
//lock on another object
lock (o)
{
System.Threading.Thread.Sleep(input);
"o".Dump();
}
}
}
public static class extenstions
{
public static void Dump(this string input)
{
Console.WriteLine(input);
}
}
}
Related
Say I have an api that takes individual get requests and batch requests:
http://myapiendpoint.com/mysuperitems/1234
and
http://myapiendpoint.com/mysuperitems/1234,2345,456,5677
and in my code I have a method for getting singles:
async Task<mysuperitem> GetSingleItem(int x) {
var endpoint = $"http://myapiendpoint.com/mysuperitems/{x}";
//... calls single request endpoint
}
but what I want to do is pool the single calls into batch calls.
async Task<mysuperitem> GetSingleItem(int x) {
//... pool this request in a queue and retrieve it when batch complete
}
async Task<IEnumerable<mysuperitem> GetMultiItem(IEnumerable<int> ids){
//... gets items and lets single item know it's done
}
how would i batch the calls asynchronously and inform the single call of completion. Thinking something with a ConcurrentQueue and Timer job?
It seems Task.WhenAll is what you need:
async Task<mysuperitem> GetSingleItem(int x)
{
return await ... // calls single request endpoint
}
async Task<IEnumerable<mysuperitem>> GetMultiItem(IEnumerable<int> ids)
{
return await Task.WhenAll(ids.Select(id => GetSingleItem(id)));
}
Yea, you can use a System.Timers with Timer.Interval.
And I'd use a normal Dictionary> to make it simple and to easily map id's to tasks, you're most likely batch all requests from that intervall anyway, so no real need for a queue. And then simply sync the GetSingleItem with the GetMultiItem called from the timer like:
private Dictionary<int,Task<mysuperitem>> _batchbuffer;
private object _lock = new object();
Task<mysuperitem> GetSingleItem(int id) {
lock(_lock) {
return _batchbuffer[id] = new Task<mysuperitem>();
}
}
async Task GetMultiItem(){
Dictionary<int,Task<mysuperitem>> temp;
lock(_lock) {
temp = new Dictionary<int,Task<mysuperitem>>(_batchbuffer);
_batchbuffer.Clear()
}
var batchResults = // do batch request for temp.Keys;
foreach(var result in batchResults)
temp[result.id].complete(result);
}
this is ofc batching to reduce server/network load, if you want to increase client performance that's something different.
this is a first hack attempt:
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using timers = System.Timers;
using System.Threading;
using System.Collections.Concurrent;
namespace MyNamespace
{
class BatchPoolConsumer<TReturn, TArgs>
{
class PoolItem
{
private readonly object _itemWriteLock = new object();
public object ItemWriteLock => _itemWriteLock;
public Task BlockingTask { get; set; }
public TReturn ReturnValue { get; set; }
public Guid BatchId { get; set; }
public bool IsRead { get; set; }
public ManualResetEventSlim Slim { get; set; }
}
private readonly timers.Timer _batchTimer;
private readonly timers.Timer _poolCleanerTimer;
private readonly ConcurrentDictionary<TArgs, PoolItem> _taskPool =
new ConcurrentDictionary<TArgs, PoolItem>();
private readonly Func<IEnumerable<TArgs>, Task<IEnumerable<(TArgs, TReturn)>>> _batchProcessor;
private readonly int _consumerMaxBatchConsumption;
public BatchPoolConsumer(Func<IEnumerable<TArgs>, Task<IEnumerable<(TArgs, TReturn)>>> batchProcessor, TimeSpan interval, int consumerMaxBatchConsumption)
{
_batchProcessor = batchProcessor;
_consumerMaxBatchConsumption = consumerMaxBatchConsumption;
_batchTimer = InitTimer(interval, BatchTimerElapsed);
_poolCleanerTimer = InitTimer(interval, PoolCleanerElapesed);
}
private static timers.Timer InitTimer(TimeSpan interval, Action<object, timers.ElapsedEventArgs> callback)
{
var timer = new timers.Timer(interval.TotalMilliseconds);
timer.Elapsed += (s, e) => callback(s, e);
timer.Start();
return timer;
}
private void PoolCleanerElapesed(object sendedr, timers.ElapsedEventArgs e)
{
var completedKeys = _taskPool
.Where(i => i.Value.IsRead)
.Select(i => i.Key).ToList();
completedKeys.ForEach(k => _taskPool.TryRemove(k, out _));
}
private void BatchTimerElapsed(object sender, timers.ElapsedEventArgs e)
{
_batchTimer.Stop();
var batchId = Guid.NewGuid();
var keys = _taskPool
.Where(i => !i.Value.BlockingTask.IsCompleted && !i.Value.IsRead && i.Value.BatchId == Guid.Empty)
.Take(_consumerMaxBatchConsumption).Select(kvp => kvp.Key);
keys.ToList()
.ForEach(k =>
{
if(_taskPool.TryGetValue(k, out PoolItem item))
{
lock (item.ItemWriteLock)
{
item.BatchId = batchId;
}
}
});
_batchTimer.Start();
if (_taskPool
.Any(pi => pi.Value.BatchId == batchId))
{
Console.WriteLine($"Processing batch {batchId} for {_taskPool.Count(pi => pi.Value.BatchId == batchId)} items");
var results = _batchProcessor(_taskPool
.Where(pi => pi.Value.BatchId == batchId)
.Select(i => i.Key)).Result;
Console.WriteLine($"Completed batch {batchId} for {_taskPool.Count(pi => pi.Value.BatchId == batchId)} items");
results.ToList().ForEach(r =>
{
if(_taskPool.TryGetValue(r.Item1,out PoolItem val))
{
lock (val.ItemWriteLock)
{
val.ReturnValue = r.Item2;
val.Slim.Set();
}
}
});
}
}
public async Task<TReturn> Get(TArgs args)
{
var slim = new ManualResetEventSlim(false);
var task = Task.Run(() =>
{
slim.Wait();
});
var output = new PoolItem
{
BlockingTask = task,
IsRead = false,
Slim = slim
};
_taskPool[args] = output;
await task;
var returnVal = output.ReturnValue;
output.IsRead = true;
return returnVal;
}
}
}
I have thread pool implementation where whenever I try to stop/join the pool there is always one random thread in the pool that will not stop (state == Running) when I call Stop() on the pool.
I cannot see why, I only have one lock, I notify whoever might be blocked waiting for Dequeue with Monitor.PulseAll in Stop. The debugger clearly shows most of them got the message, it is just always 1 out of N that is still running...
Here is a minimal implementation of the pool
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MultiThreading
{
public class WorkerHub
{
private readonly object _listMutex = new object();
private readonly Queue<TaskWrapper> _taskQueue;
private readonly List<Thread> _threads;
private int _runCondition;
private readonly Dictionary<string, int> _statistics;
public WorkerHub(int count = 4)
{
_statistics = new Dictionary<string, int>();
_taskQueue = new Queue<TaskWrapper>();
_threads = new List<Thread>();
InitializeThreads(count);
}
private bool ShouldRun
{
get => Interlocked.CompareExchange(ref _runCondition, 1, 1) == 1;
set
{
if (value)
Interlocked.CompareExchange(ref _runCondition, 1, 0);
else
Interlocked.CompareExchange(ref _runCondition, 0, 1);
}
}
private void InitializeThreads(int count)
{
Action threadHandler = () =>
{
while (ShouldRun)
{
var wrapper = Dequeue();
if (wrapper != null)
{
wrapper.FunctionBinding.Invoke();
_statistics[Thread.CurrentThread.Name] += 1;
}
}
};
for (var i = 0; i < count; ++i)
{
var t = new Thread(() => { threadHandler.Invoke(); });
t.Name = $"WorkerHub Thread#{i}";
_statistics[t.Name] = 0;
_threads.Add(t);
}
}
public Task Enqueue(Action work)
{
var tcs = new TaskCompletionSource<bool>();
var wrapper = new TaskWrapper();
Action workInvoker = () =>
{
try
{
work.Invoke();
tcs.TrySetResult(true);
}
catch (Exception e)
{
tcs.TrySetException(e);
}
};
Action workCanceler = () => { tcs.TrySetCanceled(); };
wrapper.FunctionBinding = workInvoker;
wrapper.CancelBinding = workCanceler;
lock (_taskQueue)
{
_taskQueue.Enqueue(wrapper);
Monitor.PulseAll(_taskQueue);
}
return tcs.Task;
}
private TaskWrapper Dequeue()
{
lock (_listMutex)
{
while (_taskQueue.Count == 0)
{
if (!ShouldRun)
return null;
Monitor.Wait(_listMutex);
}
_taskQueue.TryDequeue(out var wrapper);
return wrapper;
}
}
public void Stop()
{
ShouldRun = false;
//Wake up whoever is waiting for dequeue
lock (_listMutex)
{
Monitor.PulseAll(_listMutex);
}
foreach (var thread in _threads)
{
thread.Join();
}
var sum = _statistics.Sum(pair => pair.Value) * 1.0;
foreach (var stat in _statistics)
{
Console.WriteLine($"{stat.Key} ran {stat.Value} functions, {stat.Value/sum * 100} percent of the total.");
}
}
public void Start()
{
ShouldRun = true;
foreach (var thread in _threads) thread.Start();
}
}
}
With a test run
public static async Task Main(string[] args)
{
var hub = new WorkerHub();
var tasks = Enumerable.Range(0, (int) 100).Select(x => hub.Enqueue(() => Sum(x)))
.ToArray();
var sw = new Stopwatch();
sw.Start();
hub.Start();
await Task.WhenAll(tasks);
hub.Stop();
sw.Start();
Console.WriteLine($"Work took: {sw.ElapsedMilliseconds}ms.");
}
public static int Sum(int n)
{
var sum = 0;
for (var i = 0; i <= n; ++i) sum += i;
Console.WriteLine($"Sum of numbers up to {n} is {sum}");
return sum;
}
Am I missing something fundamental? Please note this is not production code (phew) but stuff I am just missing around with so you might find more than 1 issue :)
I wasn't able to repro your MCVE at first because I ran it in a non-async Main()...
If you view the 'Threads' debug window at the call to hub.Stop(); you should see that execution has switched to one of your worker threads. This is why one worker thread does not respond.
I think its related to the problem described here.
Switching Enqueue(Action work) to use TaskCreationOptions.RunContinuationsAsynchronously should fix it:
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
[Edit]
Probably a better way to avoid the problem is to swap out the direct thread management to use tasks (this isn't a proper drop-in replacement for your current code, just want to show the idea):
public class TaskWorkerHub
{
ConcurrentQueue<Action> workQueue = new ConcurrentQueue<Action>();
int concurrentTasks;
CancellationTokenSource cancelSource;
List<Task> workers = new List<Task>();
private async Task Worker(CancellationToken cancelToken)
{
while (workQueue.TryDequeue(out var workTuple))
{
await Task.Run(workTuple, cancelToken);
}
}
public TaskWorkerHub(int concurrentTasks = 4)
{
this.concurrentTasks = concurrentTasks;
}
public void Enqueue(Action work) => workQueue.Enqueue(work);
public void Start()
{
cancelSource = new CancellationTokenSource();
for (int i = 0; i < concurrentTasks; i++)
{
workers.Add(Worker(cancelSource.Token));
}
}
public void Stop() => cancelSource.Cancel();
public Task WaitAsync() => Task.WhenAll(workers);
}
I am using excel-DNA example as basis for this test.
I want excel to show my updated data in cell B1 every 1 second.
This works fine for about the first 5 secs, then I get a timer and the have to wait for function finish to see only the last value.
How do I force the update to be shown on the spreadsheet at each cycle of the loop?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ExcelDna.Integration;
using System.Threading.Tasks;
using System.Diagnostics;
namespace BTPRTD
{
class Program
{
static void Main(string[] args)
{
}
}
public static class MyFunctions
{
public static MouseData MData;
[ExcelFunction(Description = "My first .NET function")]
public static string SayHello(string name)
{
//Debugger.Launch();
MData = new MouseData ();
Task.Factory.StartNew(() =>
{
ExcelAsyncUtil.QueueAsMacro(() =>
{
KeepWritingData();
});
});
return "Hello " + name;
}
public static void KeepWritingData()
{
var refB1 = new ExcelReference(0, 0, 1, 1, "Sheet1");
string dataTxt = "";
for (int i = 0; i < 100; i++)
{
try
{
MouseData .CurrentPriceData CPD = MData.GetCurrentPriceNT("White mouse");
dataTxt = CPD.AsString();
}
catch (Exception)
{
dataTxt = "Data Unavailable";
}
refB1.SetValue("Ding: " + i + " " + dataTxt);
System.Threading.Thread.Sleep(1000);
}
}
}
}
Excel supports a type of worksheet function called RealTimeData (RTD), which is what you should use for your scenario.
Follow the examples in the "Samples" GitHub repository. In special, take a look at the RtdClocks example that uses an Observable Timer:
public static class RtdClock
{
[ExcelFunction(Description = "Provides a ticking clock")]
public static object dnaRtdClock_Rx()
{
string functionName = "dnaRtdClock_Rx";
object paramInfo = null; // could be one parameter passed in directly, or an object array of all the parameters: new object[] {param1, param2}
return ObservableRtdUtil.Observe(functionName, paramInfo, () => GetObservableClock() );
}
static IObservable<string> GetObservableClock()
{
return Observable.Timer(dueTime: TimeSpan.Zero, period: TimeSpan.FromSeconds(1))
.Select(_ => DateTime.Now.ToString("HH:mm:ss"));
}
}
I want to read and process 10+ lines at a time for GB files, but haven't found a solution to spit out 10 lines until the end.
My last attempt was :
int n = 10;
foreach (var line in File.ReadLines("path")
.AsParallel().WithDegreeOfParallelism(n))
{
System.Console.WriteLine(line);
Thread.Sleep(1000);
}
I've seen solutions that use buffer sizes but I want to read in the entire row.
The Default behavour is to read all the Line in one shot, if you want to read less than that you need to dig a little deeper into how it reads them and get a StreamReader which will then let you control the reading process
using (StreamReader sr = new StreamReader(path))
{
while (sr.Peek() >= 0)
{
Console.WriteLine(sr.ReadLine());
}
}
it also has a ReadLineAsync method that will return a task
if you contain these tasks in an ConcurrentBag you can very easily keep the processing running on 10 lines at a time.
var bag =new ConCurrentBag<Task>();
using (StreamReader sr = new StreamReader(path))
{
while(sr.Peek() >=0)
{
if(bag.Count < 10)
{
Task processing = sr.ReadLineAsync().ContinueWith( (read) => {
string s = read.Result;//EDIT Removed await to reflect Scots comment
//process line
});
bag.Add(processing);
}
else
{
Task.WaitAny(bag.ToArray())
//remove competed tasks from bag
}
}
}
note this code is for guidance only not to be used as is;
if all you want is the last ten lines then you can get that with the solution here
How to read a text file reversely with iterator in C#
This method would create "pages" of lines from your file.
public static IEnumerable<string[]> ReadFileAsLinesSets(string fileName, int setLen = 10)
{
using (var reader = new StreamReader(fileName))
while (!reader.EndOfStream)
{
var set = new List<string>();
for (var i = 0; i < setLen && !reader.EndOfStream; i++)
{
set.Add(reader.ReadLine());
}
yield return set.ToArray();
}
}
... More fun version...
class Example
{
static void Main(string[] args)
{
"YourFile.txt".ReadAsLines()
.AsPaged(10)
.Select(a=>a.ToArray()) //required or else you will get random data since "WrappedEnumerator" is not thread safe
.AsParallel()
.WithDegreeOfParallelism(10)
.ForAll(a =>
{
//Do your work here.
Console.WriteLine(a.Aggregate(new StringBuilder(),
(sb, v) => sb.AppendFormat("{0:000000} ", v),
sb => sb.ToString()));
});
}
}
public static class ToolsEx
{
public static IEnumerable<IEnumerable<T>> AsPaged<T>(this IEnumerable<T> items,
int pageLength = 10)
{
using (var enumerator = new WrappedEnumerator<T>(items.GetEnumerator()))
while (!enumerator.IsDone)
yield return enumerator.GetNextPage(pageLength);
}
public static IEnumerable<T> GetNextPage<T>(this IEnumerator<T> enumerator,
int pageLength = 10)
{
for (var i = 0; i < pageLength && enumerator.MoveNext(); i++)
yield return enumerator.Current;
}
public static IEnumerable<string> ReadAsLines(this string fileName)
{
using (var reader = new StreamReader(fileName))
while (!reader.EndOfStream)
yield return reader.ReadLine();
}
}
internal class WrappedEnumerator<T> : IEnumerator<T>
{
public WrappedEnumerator(IEnumerator<T> enumerator)
{
this.InnerEnumerator = enumerator;
this.IsDone = false;
}
public IEnumerator<T> InnerEnumerator { get; private set; }
public bool IsDone { get; private set; }
public T Current { get { return this.InnerEnumerator.Current; } }
object System.Collections.IEnumerator.Current { get { return this.Current; } }
public void Dispose()
{
this.InnerEnumerator.Dispose();
this.IsDone = true;
}
public bool MoveNext()
{
var next = this.InnerEnumerator.MoveNext();
this.IsDone = !next;
return next;
}
public void Reset()
{
this.IsDone = false;
this.InnerEnumerator.Reset();
}
}
I'm not much familiar with WinRT. I'm encountering an unexpected behavior. I've a static variable _Verses that is initialized in static constructor of class. So expected behavior is _Verses will be initialized before first reference to static method as explained in When is a static constructor called in C#?
But when I call a static async function LoadData (WinRT) I got exception.
Object Reference not set to an instance of object.
My Code is:
public VerseCollection
{
public const int TotalVerses = 6236;
static Verse[] _Verses;
static VerseCollection()
{
_Verses = new Verse[TotalVerses];
}
internal static async void LoadData(StorageFile file)
{
using (var reader = new BinaryReader(await file.OpenStreamForReadAsync()))
{
int wId = 0;
for (int i = 0; i < VerseCollection.TotalVerses; i++)
{
var retValue = new string[reader.ReadInt32()];
for (int j = 0; j < retValue.Length; j++)
retValue[j] = reader.ReadString();
_Verses[i] = new Verse(i, wId, retValue);
wId += _Verses[i].Words.Count;
}
}
}
}
public Book
{
public static async Task<Book> CreateInstance()
{
VerseCollection.LoadData(await DigitalQuranDirectories.Data.GetFileAsync("quran-uthmani.bin"));
}
}
I call the function CreateInstance as:
async void DoInit()
{
await DigitalQuran.Book.CreateInstance();
}
Same code is working in desktop but not working for WinRT. Full Code of Book Class for Desktop is here and for VerseCollection class is here
EDIT:
Complete code is here
public class Book : VerseSpan
{
public static async Task<Book> CreateInstance()
{
_Instance = new Book();
VerseCollection.LoadData(await DigitalQuranDirectories.Data.GetFileAsync("quran-uthmani.bin"));
PrivateStorage.LoadQuranObjectsFromMetadata();
// Some Other Operations too
return _Instance;
}
}
public class VerseCollection
{
static Verse[] _Verses = new Verse[TotalVerses];
internal static async void LoadData(StorageFile file)
{
using (var reader = new BinaryReader(await file.OpenStreamForReadAsync()))
{
int wId = 0;
for (int i = 0; i < VerseCollection.TotalVerses; i++)
{
var retValue = new string[reader.ReadInt32()];
for (int j = 0; j < retValue.Length; j++)
retValue[j] = reader.ReadString();
_Verses[i] = new Verse(i, wId, retValue);
wId += _Verses[i].Words.Count;
}
}
}
}
public class Verse
{
public Verse(int number, int firstWordIndex, string[] words)
{
GlobalNumber = number + 1;
Words = new WordCollection(firstWordIndex, words, this);
}
}
public class WordCollection : ReadOnlyCollection<Word>
{
public const int TotalWords = 77878;
static Word[] _Words = new Word[TotalWords];
static string[] _WordsText = new string[TotalWords];
public WordCollection(int startIndex, int count)
: base(count)
{
this.startIndex = startIndex;
}
internal WordCollection(int startId, string[] words, Verse verse) : this(startId, words.Length)
{
int max = words.Length + startId;
for (int i = startId; i < max; i++)
{
_Words[i] = new Word(i, verse);
_WordsText[i] = words[i - startId];
}
}
}
public abstract class ReadOnlyCollection<T> : IEnumerable<T>
{
public ReadOnlyCollection(int count)
{
Count = count;
}
}
public class PrivateStorage
{
internal static async void LoadQuranObjectsFromMetadata()
{
using (var reader = new BinaryReader(await (await DigitalQuranDirectories.Data.GetFileAsync(".metadata")).OpenStreamForReadAsync()))
{
/* 1 */ ChapterCollection.LoadData(EnumerateChapters(reader));
/* 2 */ PartCollection.LoadData(EnumerateParts(reader));
/* Some other tasks */
}
}
static IEnumerator<ChapterMeta> EnumerateChapters(BinaryReader reader)
{
for (int i = 0; i < ChapterCollection.TotalChapters; i++)
{
yield return new ChapterMeta()
{
StartVerse = reader.ReadInt32(),
VerseCount = reader.ReadInt32(),
BowingCount = reader.ReadInt32(),
Name = reader.ReadString(),
EnglishName = reader.ReadString(),
TransliteratedName = reader.ReadString(),
RevelationPlace = (RevelationPlace)reader.ReadByte(),
RevelationOrder = reader.ReadInt32()
};
}
}
static IEnumerator<PartMeta> EnumerateParts(BinaryReader reader)
{
for (int i = 0; i < PartCollection.TotalParts; i++)
{
yield return new PartMeta()
{
StartVerse = reader.ReadInt32(),
VerseCount = reader.ReadInt32(),
ArabicName = reader.ReadString(),
TransliteratedName = reader.ReadString()
};
}
}
}
public class ChapterCollection : ReadOnlyCollection<Chapter>
{
public const int TotalChapters = 114;
static Chapter[] _Chapters = new Chapter[TotalChapters];
internal static void LoadData(IEnumerator<ChapterMeta> e)
{
for (int i = 0; i < TotalChapters; i++)
{
e.MoveNext();
_Chapters[i] = new Chapter(i, e.Current);
}
}
}
public class PartCollection : ReadOnlyCollection<Part>
{
public const int TotalParts = 30;
static Part[] _Parts = new Part[TotalParts];
internal static void LoadData(IEnumerator<PartMeta> e)
{
for (int i = 0; i < TotalParts; i++)
{
e.MoveNext();
_Parts[i] = new Part(i, e.Current);
}
}
}
When I run the code with debugger no exception is raised. Further After exception visual studio shows some times in class VerseCollection in function LoadData on line _Verses[i] = new Verse(i, wId, retValue); (_Verses is null) and some times in class ChapterCollection in Function LoadData on line _Chapters[i] = new Chapter(i, e.Current); (_Chapters is null)
There was issue with asynchronous call. File reading is asynchronous operation in WinRT. As We can't call async method with void return type with await statement. So next instructions executes without waiting for completion of last executing as another Task. This leads to NullReferanceExecption.
I managed to solve my problems by changing return type of all async operations from void to Task and called them with await like in the code below.
public class Book : VerseSpan
{
public static async Task<Book> CreateInstance()
{
_Instance = new Book();
await VerseCollection.LoadData(await DigitalQuranDirectories.Data.GetFileAsync("quran-uthmani.bin"));
await PrivateStorage.LoadQuranObjectsFromMetadata();
// Some Other Operations too
return _Instance;
}
}
public class VerseCollection
{
static Verse[] _Verses = new Verse[TotalVerses];
internal static async Task LoadData(StorageFile file)
{
using (var reader = new BinaryReader(await file.OpenStreamForReadAsync()))
{
int wId = 0;
for (int i = 0; i < VerseCollection.TotalVerses; i++)
{
var retValue = new string[reader.ReadInt32()];
for (int j = 0; j < retValue.Length; j++)
retValue[j] = reader.ReadString();
_Verses[i] = new Verse(i, wId, retValue);
wId += _Verses[i].Words.Count;
}
}
}
}
public class PrivateStorage
{
internal static async Task LoadQuranObjectsFromMetadata()
{
using (var reader = new BinaryReader(await (await DigitalQuranDirectories.Data.GetFileAsync(".metadata")).OpenStreamForReadAsync()))
{
/* Some tasks */
}
}
}
Because it is running on Desktop but not WinRT, it leads me to believe there is an issue with your asynchronous call. Because you are doing this asynchronously, there is no gaurantee that the constructor (static or not) will be finished running before the call to LoadData. Make sure that your constructor has finished executing before calling LoadData function, and this should give you consistent behaviour.