I wrote a simple test program which adds 3000 two-column rows to a ListView. In the device emulator, the 3000 rows are added in about 7 seconds. On a real device, filling the ListView will be faster. Note that I did not use a separate thread.
I can think of at least a couple reasons for the slowness you experience.
First, if many temporary objects are being created as you read the data and/or fill the ListView, then this could cause slow down. Object allocation is expensive, and so is garbage collection -- which may occur frequently if a lot of temporary objects are created. Temporary objects may get created in many different ways, so it is important to review your code to see if it is doing wasteful things.
Second, thread synchronization via Control.Invoke() can be very slow, especially if you are doing this for every single returned row. Unless there is a good reason to be filling the ListView as each row arrives from the database query, then you would be better off taking a different approach. For instance, you could fill the ListView in batches, say, use a single Invoke() to add a batch each time 100 records is retrieved from the database. Even better, would be to wait for the entire query to complete and then use Invoke() just once to fill the ListView in one step.
Probably best, however, would be to eliminate the second thread altogether, and do the query and ListView population in the main thread. If, on average, your query takes only several seconds (5-20) and filling the ListView, on average, takes only several seconds (1-10), then it would be sufficient (and much simpler) to do all the work in the main thread.