11

How can I make make the following code run asynchronously without having to create an extra thread on the thread pool (in other words without Task.Run(...))?

Directory.CreateDirectory("\\host\someNetworkDirectory");

Ideally, I would like to use it like this:

async SomeMethod(){
    //...
    await Directory.CreateDirectoryAsync("\\host\someNetworkDirectory");
    // stuff to do after ensuring the directory exists
    //...
}

I tried this answer (which suggest using FileSystemWatcher) but it does not work for folders.

user247702
  • 23,641
  • 15
  • 110
  • 157
brakeroo
  • 1,407
  • 13
  • 24
  • 1
    Link you have posted is the best way to implement an IO task using `TaskCompletionSource` and get notified, once the directory is created, most of the IO async methods use it internally. There's nothing complex about it. – Mrinal Kamboj Jan 18 '17 at 01:03
  • 1
    In fact, what your posted code does is use the `FileSystemWatcher` for Async processing, which monitors the location for directory creation to notify your program, but you seem to be programmatically creating directory, which seems to be a different requirement – Mrinal Kamboj Jan 18 '17 at 01:13
  • 2
    There's no way to accomplish what you want without doing *something* on another thread. The underlying Windows API doesn't support asynchronous directory creation. – Collin Dauphinee Jan 18 '17 at 23:26
  • 1
    You don't need an FSW to create a directory or a thread. It's just a metadata operation that costs a lot *less* than starting the task. That's why it doesn't have an asynchronous equivalent. FSWs are used to detect *future* changes, eg when someone copies another file into a folder. – Panagiotis Kanavos Jan 19 '17 at 09:15
  • 4
    This sounds like a case of the [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). You have a problem X and try to solve it through Y. When you run into trouble with Y, you ask about Y, not the actual problem X. Why do you think you need to create a folder asynchronously? – Panagiotis Kanavos Jan 19 '17 at 09:21
  • 1
    @PanagiotisKanavos I think I need to create a folder asynchronously because the calling thread (happens to be the UI) blocks to create a folder. In certain situations (when network slow, etc) is noticeable and UI freezes. Most of the work of creating a folder is handled by external systems (OS, network) and my app blocks waiting for a response from them hence a need for async. I understand that creating a folder is a fast operation (most of the time) but in my case it isn't. I guess WinRT has a CreateFolderAsync for the same reason but unfortunately I'm not on RT. – brakeroo Jan 19 '17 at 22:39
  • 1
    @PanagiotisKanavos May I ask how would you rephrase the question so it's not a classic case of XY (which I don't think it is) ? – brakeroo Jan 19 '17 at 22:40
  • 1
    *Why* is the operation slow? That's the root of the problem. It shouldn't be slow on a local driver. Working on network shares could be slower but not so it's noticeable on the UI. Working with *WebDAV* as a network share, eg with SharePoint libraries is going to be noticeable. Better to use the SharePoint client or a WebDAV library in this case. – Panagiotis Kanavos Jan 20 '17 at 07:57
  • 4
    @brakeroo are you targeting some slow network storage device perhaps? These can be underpowered or have problems with their SMB implementations. That could also cause delays. In that case `Task.Run` may be the only practical option - even the Win32 API doesn't have asynchronous directory methods. *Maybe* you could find some network-specific functions, but they probably wouldn't be worth it – Panagiotis Kanavos Jan 20 '17 at 08:15
  • 1
    @PanagiotisKanavos If I find out the answer to the "Why is the operation slow?" it does not help me all because I have zero control on changing the part of the process dealing with how/where to save the folder. The **only** thing I can change is the code that is attempting to save the folder. So I still can't see how I can ask this question so it's not "a classic case of XY problem" and actually get help with my issue. `TLDR`: Finding an answer to "why the operation is slow?" does not help with my actual problem. – brakeroo Jan 20 '17 at 16:16
  • 1
    @MachineLearning I disagree. I cannot create anything on the storage server nor change any part of the process. Not sure why you're assuming what you're assuming but in the end my problem is not solved by finding out why the network is slow. Your last sentence seems to suggest I ask the question "Why am I not allowed to change XCompany way of doing thing?" - seems like a ridiculous question. – brakeroo Jan 21 '17 at 19:48

2 Answers2

12

At the OS level, this function is strictly synchronous. WinAPI only has two functions for creating directories, CreateDirectory and CreateDirectoryEx. Neither of them accepts a callback.

Therefore, there is nothing you can do to avoid having to create a thread to make this asynchronous. Best you can hope for is to find a library which hides this away from you.

Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
6

Look at the static Task.Run methods.

async SomeMethod() {
    //...
    await Task.Run(() => Directory.CreateDirectory("\\host\someNetworkDirectory"));
    //...
}
ILMTitan
  • 10,751
  • 3
  • 30
  • 46
  • 1
    It is always preferred to do such IO tasks using `TaskCompletionSource`, instead of invoking a thread pool thread using `Task.Run`, which is sort of fake wrapper for actual work – Mrinal Kamboj Jan 18 '17 at 01:00
  • 5
    @MrinalKamboj you misunderstand what Task.Run or TCS do. Nothing is a fake wrapper. It's TCS that wraps other asynchronous APIs and exposes them as tatsk. The TCS itself won't do anything. The code will still run on the original thread. – Panagiotis Kanavos Jan 19 '17 at 09:17
  • 1
    @ PanagiotisKanavos, I think I should have clarified my point better, in my view to make a method Async especially related to IO (which is actually not required, as clear from the original question comments), `TaskCompletionSource` as an option to expose a method a Task is a better choice than wrapping a Synchronous operation in the `Task.Run`, I see most of the IO Async methods implemented using TCS – Mrinal Kamboj Jan 19 '17 at 11:32
  • 4
    @MrinalKamboj you misunderstood those examples. TCS is used to wrap other APIs, ie EAP (event-based) or APM (Begin/End with callback). It's that other API that performs the asynchronous job. When that API finishes, *it* signals the TCS from whatever thread it decides to use. The TCS is created and returned by the original thread. If you tried to use it in conjunction with `CreateDirectory` you'd still block the original thread. – Panagiotis Kanavos Jan 19 '17 at 12:08
  • 2
    @PanagiotisKanavos, agreed, I have not misunderstood, I'm aware the usage of TCS with Events based APIs, just that question and answer were both misleading, which added to my confusion, as you have mentioned `CreateDirectory` doesn't need `Async` at all, it doesn't expose any event, it is different from `FileWatcher` as shown in the link and surely wrapping it in the `Task`, as shown in the answer is surely a poor choice – Mrinal Kamboj Jan 19 '17 at 13:29